You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅在模板类成员函数合法时对其进行ODR使用(C++11)

问题:C++11中条件式ODR使用类模板成员函数

我需要在类模板foo_t上定义成员函数__say_hi,该函数不会被任何地方调用,但必须被基于Clang的前端保留。为此添加了持有该函数地址的constexpr静态成员,但如果模板参数不满足条件(如T不可拷贝),__say_hi的函数体无法编译,导致整个模板实例化失败。

要求仅当__say_hi可正常编译时,才定义这个持有其地址的静态成员;且foo_t是库组件,无法提前为所有可能的实例化类型编写特化。

示例代码(存在编译问题):

template<typename T>
struct foo_t {

  const char * __say_hi() const { 
     T copy(m_t); // 需要T具备拷贝构造函数
     return "hi";
  } // 该函数不会被调用,但不能从AST中移除

   static constexpr auto __ODR_use = &foo_t::__say_hi; // ODR使用以保留函数

  T m_t;
};

struct non_copyable_t { 
  non_copyable_t(non_copyable_t const &) = delete;
};

int main() {
   foo_t<int> f1; // 正常
   // foo_t<non_copyable_t> f2; // 编译错误
}

解决方案

核心思路是通过SFINAE检测__say_hi是否可合法实例化,再基于检测结果通过继承条件基类的方式,选择性定义ODR使用的静态成员。这样既避免非法实例化时的编译错误,又能在合法时保留函数。

完整代码:

#include <type_traits>
#include <utility>

template<typename T>
struct foo_t;

// 检测foo_t<T>::__say_hi是否可合法实例化的traits
template<typename T>
struct has_valid_say_hi {
private:
    // 尝试构造__say_hi的调用表达式,若合法则返回std::true_type
    template<typename U>
    static auto test(int) -> decltype(std::declval<const foo_t<U>&>().__say_hi(), std::true_type{});
    
    // 匹配所有不满足条件的情况,返回std::false_type
    template<typename U>
    static std::false_type test(...);
public:
    static constexpr bool value = decltype(test<T>(0))::value;
};

// 条件基类:仅当检测通过时提供__ODR_use成员
template<typename T, bool = has_valid_say_hi<T>::value>
struct odr_use_base {};

template<typename T>
struct odr_use_base<T, true> {
    static constexpr auto __ODR_use = &foo_t<T>::__say_hi;
};

template<typename T>
struct foo_t : odr_use_base<T> {
    const char * __say_hi() const { 
        T copy(m_t);
        return "hi";
    }

    T m_t;
};

struct non_copyable_t { 
    non_copyable_t(non_copyable_t const &) = delete;
};

int main() {
    foo_t<int> f1; // 正常,__ODR_use被定义,__say_hi被保留
    foo_t<non_copyable_t> f2; // 正常,无__ODR_use,__say_hi不会被实例化
}

原理说明

  1. traits检测:has_valid_say_hi通过SFINAE机制,尝试在编译时构造__say_hi的调用表达式。若表达式合法(即__say_hi的函数体可正常编译),则test<int>重载被选中,返回std::true_type;否则匹配test(...)重载,返回std::false_type。
  2. 条件继承:odr_use_base根据检测结果做模板特化:当检测通过时,特化版本定义__ODR_use静态成员,对__say_hi进行ODR使用;否则基类为空,不定义该成员。
  3. 避免不必要实例化:当__ODR_use未定义时,__say_hi不会被ODR使用,其函数体也不会被编译器实例化,从而避免非法模板参数带来的编译错误。

内容的提问来源于stack exchange,提问作者Dorian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 09:37:08