带有enable_if的g++和clang++之间的区别



我想写一个函数,它返回一个类型T的实例,但根据T的构造方式,它的行为会有所不同。假设我有这样的结构

#include <type_traits>                                                                                                                                                                                                                                                                                                        
#include <iostream>
struct A {}; 
struct B {}; 
struct C { 
C(A a) {
std::cout << "C" << std::endl;
}
};

我想通过给它们一个A来创建Cs。我有一个类似的结构,它使用enable_if来选择两个函数之一:

struct E {
template< bool condition = std::is_constructible<C, A>::value,std::enable_if_t<condition,int> = 0>
C get() {
return C{A{}};
}
template< bool condition = std::is_constructible<C, B>::value,std::enable_if_t<condition,bool> = false>
C get() {
return C{B{}};
}
};

这与g++82(我认为也是g++9(一起编译得很好,但clang9给了我错误

$ clang++ --std=c++17 main.cpp 
main.cpp:26:12: error: no matching constructor for initialization of 'C'
return C{B{}};
^~~~~~
main.cpp:6:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'B' to 'const C' for 1st argument
struct C {
^
main.cpp:6:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'B' to 'C' for 1st argument
struct C {
^
main.cpp:7:3: note: candidate constructor not viable: no known conversion from 'B' to 'A' for 1st argument
C(A a) {
^
1 error generated.

即使enable_if应该隐藏该函数。(我叫E e; auto c = e.get();(。如果我不硬编码C,而是使用模板传入C,那么它在两个编译器中都有效。

template<typename T>
struct F {
template< bool condition = std::is_constructible<T, A>::value,std::enable_if_t<condition,int> = 0>
T get() {
return T{A{}};
}
template< bool condition = std::is_constructible<T, B>::value,std::enable_if_t<condition,bool> = false>
T get() {
return T{B{}};
}
};

我不明白为什么clang显然会对函数体进行类型检查,尽管函数应该被enable_if禁用。

两个编译器都是正确的,

http://eel.is/c++吃水深度/温度res#8.1

模板的有效性可以在任何实例化之前进行检查。[注意:知道哪些名称是类型名称,就可以用这种方式检查每个模板的语法。--结束注释]程序格式错误,不需要诊断,如果:

(8.1(
-如果模板中的语句未实例化,则不能为模板或constexpr的子语句生成有效的专门化,或者

[..]

(8.4(
-由于不依赖于模板参数或的构造,模板定义后的假设实例化将是不正确的

return C{B{}};不依赖于模板,并且是错误的。clang通过诊断这个问题是好的。

由于您似乎可以访问支持c++17的编译器,因此可以使用if constexpr而不是enable_if来实现您想要的功能。

#include <iostream>
#include <type_traits>
struct A {};
struct B {};
struct C {
explicit C(A a) {
std::cout << "C" << std::endl;
}
};
template<typename T>
struct False : std::false_type {};
struct E {
template<typename T = void>
C get() const {
if constexpr (std::is_constructible_v<C, A>) {
return C{A{}};
} else if constexpr (std::is_constructible_v<C, B>) {
return C{B{}};
} else {
static_assert(False<T>::value, "Error");
}
}
};
int main() {
const auto C{E{}.get()};
return 0;
}

最新更新