以下程序在使用GCC 4.7和clang 3.2编译时,产生"1"作为输出。
#include <type_traits>
struct foo {
template<typename T>
foo(T) {
static_assert(not std::is_same<int, T>(), "no ints please");
}
};
#include <iostream>
int main() {
std::cout << std::is_constructible<foo, int>();
}
这是令人困惑的。 foo
显然无法从int
中构建!如果我将main
更改为以下内容,则由于静态断言失败,两个编译器都拒绝它:
int main() {
foo(0);
}
为什么两个编译器都说它是可构造的?
这就是标准(§20.9.5/6(的内容,我强调:
给定以下函数原型:
template <class T> typename add_rvalue_reference<T>::type create();
模板专用化的谓词条件
is_constructible<T, Args...>
当且仅当 以下变量定义对于某些发明的格式很好 可变t
:T t(create<Args>()...);
[ 注意:这些标记永远不会被解释为函数 声明。—尾注 ]
访问检查的执行就像在与
T
和 任何Args
.只有直接上下文的有效性 考虑变量初始化。[注:评价 初始化可能会导致副作用,例如实例化 类模板专用化和函数模板专用化, 生成隐式定义的函数,等等。这样的一面 效果不在"直接上下文"中,可能导致 程序格式不正确。—尾注 ]
仅当实例化模板构造函数时,断言才会失败。然而,正如说明中所澄清的,这种断言不在所考虑的变量定义的直接上下文中,因此不影响其"有效性"。因此编译器可以将该定义视为有效,从而声称foo
确实可以从int
构造,即使实际上试图从int
构造一个foo
会导致程序格式错误。
请注意,编译器也允许编译器根据断言拒绝原始程序,而不是is_constructible
产生假,即使两者都没有。
foo2
是你的foo
。 foo1
是一个 foo 可以做您希望您的foo
做的事情。
#include <type_traits>
#include <utility>
struct foo1 {
template<typename T,typename=typename std::enable_if< !std::is_same<int, T>::value >::type>
foo1(T) {
static_assert(not std::is_same<int, T>(), "no ints please");
}
};
struct foo2 {
template<typename T>
foo2(T) {
static_assert(not std::is_same<int, T>(), "no ints please");
}
};
#include <iostream>
int main() {
std::cout << std::is_constructible<foo1, int>();
std::cout << std::is_constructible<foo2, int>();
}