17

以下程序在使用 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);
}

为什么两个编译器都说它是可构造的?

4

2 回答 2

24

这就是标准必须说的(§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,即使实际上尝试foo从a 构造 aint会导致格式错误的程序。

请注意,编译器也可以is_constructible根据断言拒绝原始程序,而不是让它们产生错误,即使两者都没有。

于 2013-02-11T16:06:45.040 回答
5

foo2是你的foofoo1是一个做你想做的事的 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>();
}
于 2013-02-11T20:22:11.947 回答