2016-09-22 13 views
0

次の質問は、はるかに大きなコードから集約されています。したがって、いくつかの式は過度のまたは不必要であるように見えますが、元のコードには重要です。コンパイル時のテンプレートとconstexprの控除コンパイラと最適化フラグによって異なります

は時定数と、単純なコンテナクラスをコンパイル含まれている構造体を有する考えてみましょう:

template<typename T> struct CONST 
{ 
    static constexpr T ONE() 
    { 
     return static_cast<T>(1); 
    } 
}; 

template<typename T> class Container 
{ 
public: 
    using value_type = T; 
    T value; 
}; 

は今value_typeを提供するタイプのための「専門」を持つテンプレート機能を有する:

template<typename T> void doSomething(const typename T::value_type& rhs) 
{} 

今、私は、これが動作する必要があることを、期待:

template<typename T> class Tester 
{ 
public: 
    static constexpr T ONE = CONST<T>::ONE(); 

    void test() 
    { 
     doSomething<Container<T>>(ONE); 
    } 
}; 

興味深い点は、コンパイラはTester<T>::ONEの定義に不満はないが、その使用法です。さらに、CONST<T>::ONE()、またはONEの代わりにstatic_cast<T>(ONE)を関数呼び出しで使用すると、それは不平を言っていません。しかし、どちらもコンパイル時に認識され、したがって使用可能でなければなりません。 私の最初の質問は次のとおりです。コンパイラは、コンパイル時に計算を行うケースでも、どこで動作しますか?

-std=c++14フラグを使用して、g++-5,g++-6clang-3.8コンパイラで確認しました。彼らはすべて、私が知っている限り、標準で使用されているため、サポートされている必要がありますが、

undefined reference to `Tester<int>::ONE' 

でも文句を言います。興味深いことに、最適化フラグO1O2またはO3を追加するとすぐに、コンパイルは成功します。だから私の2番目の質問です:最適化フラグがアクティブな場合、コンパイル時の計算を行うだけのコンパイラの戦略はありますか?私は、少なくともコンパイル時定数として宣言されているものは、常にと推測されました!

私の質問の最後の部分は、NVIDIA nvccコンパイラ(バージョン8.0)です。 -std=c++11にしか渡すことができないため、一部の機能は一般的にはカバーされていない場合があります。しかし、上記のホストコンパイラの1つを使用すると、最適化フラグが渡されても、

が報告されます。これは明らかに上記と全く同じ問題ですが、上記の質問はもっと学問的ですが(問題を解決するために単純に最適化フラグを使用できるため)、ここでは本当に問題です(私には分かりません上記の回避策を使用するとコンパイル時に何が行われますか?またこれも醜いです)。だから私の3番目の質問です:デバイスコードでも最適化を使用する方法はありますか?事前に

#include <iostream> 
#include <cstdlib> 

#ifdef __CUDACC__ 
    #define HD __host__ __device__ 
#else 
    #define HD 
#endif 


template<typename T> struct CONST 
{ 
    HD static constexpr T ONE() 
    { 
     return static_cast<T>(1); 
    } 
}; 


template<typename T> class Container 
{ 
public: 
    using value_type = T; 
    T value; 
}; 


template<typename T> HD void doSomething(const typename T::value_type& rhs) {} 


template<typename T> class Tester 
{ 
public: 
    static constexpr T ONE = CONST<T>::ONE(); 

    HD void test() 
    { 
     doSomething<Container<T>>(ONE); 
     // doSomething<Container<T>>(static_cast<T>(ONE)); 
     // doSomething<Container<T>>(CONST<T>::ONE()); 
    } 
}; 


int main() 
{ 
    using t = int; 

    Tester<t> tester; 
    tester.test(); 

    return EXIT_SUCCESS; 
} 

ありがとう:

次のコードは、NVCCコンパイラのため、純粋なホストのMWEとです!

+0

あなたの質問の最初の部分について見http://stackoverflow.com/questions/8452952/c-linker-error-with-class-static-constexpr –

答えて

4

この違い:これら二つのとは対照的に

doSomething<Container<T>>(ONE); 

doSomething<Container<T>>(static_cast<T>(ONE)); 
doSomething<Container<T>>(CONST<T>::ONE()); 

が最初のケースでは、あなたがONEに直接参照を結合しているし、他の人はあなたがいないということです。具体的には、最初のケースではodr-ONEですが、他の2つではありません。 odr-エンティティを使用する場合は、定義が必要で、ONEが現在宣言されていますが、定義されていません。

あなたはそれを定義する必要があります。

template<typename T> 
class Tester 
{ 
public: 
    // declaration 
    static constexpr T ONE = CONST<T>::ONE(); 
    // .. 
}; 

// definition 
template <typename T> 
constexpr T Tester<T>::ONE; 
+0

どうも。これは間違いなく、CPUの場合の答えです。しかし、私が間違って何かをしていない限り、それは 'nvcc'コンパイラではまだ動作しません。状況は同じでなければならないのですか? – marlam

+0

私はちょうど認識しています:なぜそれは最適化フラグで動作しましたか?彼らはちょうど正しいことを推測していますか(もっと複雑なケースでは間違っている可能性もありますか)、それとも何か他のことが起こっていますか? – marlam

+0

@marlam nvccでお手伝いできません。分かりません。他のほとんどのodr違反のように、それは今までに働くべきではなく、実際には悪意のあるNDRである可能性があります。 – Barry

関連する問題