17

次のプログラムは、GCC4.7とclang3.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に構築可能であると主張できます。intfooint

コンパイラーは、is_constructibleyield falseの代わりに、どちらも拒否しない場合でも、アサーションに基づいて元のプログラムを拒否することも許可されていることに注意してください。

于 2013-02-11T16:06:45.040 に答える
5

foo2あなたfooです。 foo1あなたがしたいことをするfoofooです。

#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 に答える