2013-03-05 14 views
30

私はC++コードの一部をアップグレードして、C++ 11の新機能を利用しています。ほとんどの場合(常にではありませんが)定数式を返す基本型を返すいくつかの関数を持つtraitクラスがあります。私はその機能がconstexprであるかどうかに基づいて異なることをしたいと思います。私は、次のアプローチを思い付いた:両方の機能がSFINAE後に使用可能である場合、最初のものは、解像度を過負荷によって選ばれますようにSFINAEでconstexprを検出する

template<typename Trait> 
struct test 
{ 
    template<int Value = Trait::f()> 
    static std::true_type do_call(int){ return std::true_type(); } 

    static std::false_type do_call(...){ return std::false_type(); } 

    static bool call(){ return do_call(0); } 
}; 

struct trait 
{ 
    static int f(){ return 15; } 
}; 

struct ctrait 
{ 
    static constexpr int f(){ return 20; } 
}; 

int main() 
{ 
    std::cout << "regular: " << test<trait>::call() << std::endl; 
    std::cout << "constexpr: " << test<ctrait>::call() << std::endl; 
} 

余分int/...パラメータがあります。

コンパイルとクラン3.2ショーでこれを実行している:

regular: 0 
constexpr: 1 

だから、これは動作しているように見えますが、私はコードは法的C++ 11であるかどうかを知りたいです。特に、SFINAEのルールが変更されていることを理解しているからです。

+0

興味深い問題。私はそれが合法であることを証明していると思っていた答えを書いたが、その後、私の答えに従って等しく有効であったはずの[修正版](http://liveworkspace.org/code/SWmBI$5)を書いた。どのコンパイラでもコンパイルされません。だから私は答えを提出するつもりはないが、私は非常に興味がある。 –

+3

関連:[is_constexprはC++ 11でも可能ですか?](http://stackoverflow.com/questions/13299394/is-is-constexpr-possible-in-c11)また、[デフォルトテンプレート引数のconstexprの呼び出し](http://stackoverflow.com/questions/10721130/calling-constexpr-in-default-template-argument)を参照してください。 –

+0

@AndyProwl:私は不思議です、liveworkspace.orgや他の同様のサイトにあなたのソリューションを投稿することはできますか? –

答えて

13

注:のOPコードが実際に有効であるかどうかについてI opened a question here。私の書き直した例はどんな場合でも動作します。


が、私はコードは、デフォルトのテンプレート引数が少し変わったと考えることができるが、それは、あるC++ 11

合法であるかどうかを知りたいです。私個人的には次のスタイル、より良い、あなたはどのように似ているような(読み:私は)ちょうど非型テンプレートパラメータを使用してdecltypeを残し、check for a function's existenceに形質を書く:

#include <type_traits> 

namespace detail{ 
template<int> struct sfinae_true : std::true_type{}; 
template<class T> 
sfinae_true<(T::f(), 0)> check(int); 
template<class> 
std::false_type check(...); 
} // detail:: 

template<class T> 
struct has_constexpr_f : decltype(detail::check<T>(0)){}; 

Live example.


説明時間〜

のインスタンスのデフォルトのテンプレート引数のポイントはinstantiatiのポイントですので、あなたのオリジナルのコードが†作品あなたの場合はmainという意味のその関数テンプレートの上にあるので、それより早く代用することはできません。

§14.6.4.1 [temp.point] p2

[...] [...]その関数テンプレートのデフォルト引数の定義を使用した方法で呼び出された関数テンプレート、デフォルトのインスタンス化の時点であれば引数は関数テンプレート[...]のインスタンス化のポイントです。

その後、通常のSFINAEルールです。私はそう思うAtleastの†


は、それが標準で完全明確ではありません。

+0

標準からの引用がすべての疑問を解決します。ありがとうございます。 –

+0

+1また、代替ソリューションのため –

+0

残念ながら私はそれがはっきりしているとは思わない。 C++ 03の関数に対して "デフォルトのテンプレート引数"を指定することはできませんでしたが、そのテキストもそこに存在していました。このテキストは、関数のデフォルト引数についてのみ述べており、クラステンプレートには言及していません。それはテンプレート引数については話しませんが、関数引数についてはそれが信じられません。 –

1

@ marshall-clowがプロンプトを出しましたが、私はconstexprを検出するための型特性の幾分より一般的なバージョンをまとめました。私はstd::invoke_resultでモデル化しましたが、constexprは入力に依存しているため、テンプレート引数は型ではなく渡された値です。

テンプレートの引数はlimited set of typesに限られているため、メソッド呼出時にはすべてconstになります。他のタイプが必要な場合はconstexprラッパーメソッドを簡単にテストできます。非const値の場合は、参照パラメータとしてを参照してください。

実際に便利なコードよりも多少運動とデモンストレーションがあります。

template<auto F, auto... Args>を使用すると、gcc 7またはclang 4が必要なC++ 17のみになります.MSVC 14.10.25017はコンパイルできません。

namespace constexpr_traits { 

namespace detail { 

// Call the provided method with the provided args. 
// This gives us a non-type template parameter for void-returning F. 
// This wouldn't be needed if "auto = F(Args...)" was a valid template 
// parameter for void-returning F. 
template<auto F, auto... Args> 
constexpr void* constexpr_caller() { 
    F(Args...); 
    return nullptr; 
} 

// Takes a parameter with elipsis conversion, so will never be selected 
// when another viable overload is present 
template<auto F, auto... Args> 
constexpr bool is_constexpr(...) { return false; } 

// Fails substitution if constexpr_caller<F, Args...>() can't be 
// called in constexpr context 
template<auto F, auto... Args, auto = constexpr_caller<F, Args...>()> 
constexpr bool is_constexpr(int) { return true; } 

} 

template<auto F, auto... Args> 
struct invoke_constexpr : std::bool_constant<detail::is_constexpr<F, Args...>(0)> {}; 

} 

Live demo with use-cases on wandbox

関連する問題