2016-09-22 33 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()或者甚至static_cast<T>(ONE)而不是ONE,它並不會抱怨。但是,兩者都應在編譯時知道,因此可用。 所以我的第一個問題是:編譯器在這種情況下,它在哪裏工作,甚至在編譯時進行計算?

我用g++-5,g++-6和使用-std=c++14標誌的clang-3.8編譯器檢查了它。他們都抱怨

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

雖然所有使用的功能,據我所知,在標準,因此應該支持。有趣的是,只要我添加優化標記O1,O2O3,編譯就會成功。所以我的第二個問題是:如果優化標誌處於活動狀態,是否有編譯器只進行編譯時計算的策略?我會期望至少東西,這是宣佈爲編譯時間常數總是演繹!

我的問題的最後部分涵蓋了NVIDIA nvcc編譯器(版本8.0)。由於我只能通過-std=c++11,它可能是一些功能通常不包括在內。但是,使用上面的主機編譯器之一,它會抱怨

error: identifier "Tester<int> ::ONE" is undefined in device code 

即使通過優化標誌!這顯然是與上面相同的問題,但上述問題更具有學術性(因爲我可以簡單地使用優化標記來擺脫問題),但這真的是一個問題(關於我不知道的事實,當我使用上面提到的解決方法時,在編譯時做了什麼 - 這也是醜陋的)。所以我的第三個問題是:在設備代碼中是否還有一種使用優化的方法?

下面的代碼是純主機的MWE,也爲NVCC編譯:提前

#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; 
} 

謝謝!

+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在第一種情況下,但不是其他兩個。當你使用odr-use一個實體時,它需要一個定義,並且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