继承和is_detected_v提供了一个奇怪的结果(C++ 17)

Duc*_*van 6 c++ templates c++17

我有简化的代码版本:

#include <experimental/type_traits>

template<class T> using has_data_t = decltype(T::data());

template <class B> constexpr auto get_data() {
    return std::experimental::is_detected_v<has_data_t, B>;
}

template <typename Topt> struct opt_base {
    static constexpr bool i = get_data<Topt>();
  //static constexpr auto j = get_data<Topt>(); // fail to compile
};

struct opt : public opt_base<opt> {
    static int data() { return 7;}
};

int main() {
    static_assert(std::experimental::is_detected_v<has_data_t, opt>);
}
Run Code Online (Sandbox Code Playgroud)

这段代码编译.但是,如果您取消注释注释行,则断言失败.它用GCC 7.1和Clang 4.0.0进行了测试.编译参数:-std = c ++ 1z -O3 -Wall. 演示

Bar*_*rry 7

在这段代码中:

static constexpr bool i = get_data<Topt>();
static constexpr auto j = get_data<Topt>();
Run Code Online (Sandbox Code Playgroud)

Topt(即opt)尚未完成.所以is_detected_v<has_data_t, opt>是应该false.但到我们到达时main,opt 已经完成了.所以我们期待is_detected_v<has_data_t, opt>成为true.

拥有一个模板,当在不同的上下文中实例化时产生不同的结果意味着您的程序格式错误,无需诊断.见[temp.point]:

任何模板的特化可以在多个翻译单元中具有实例化点.如果两个不同的实例化点根据单定义规则给出模板特化的不同含义,则程序形成错误,不需要诊断.

添加j并不重要 - 它恰好翻转了开关,改变了编译器选择实例化方式的顺序.无论是否存在,该计划都是不完整的j.