编译时的模板和constexpr演绎取决于编译器和优化标志

时间:2016-09-22 14:56:03

标签: c++ templates c++11 cuda 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;
};

现在有一个模板功能,它有一个&#34;专业化&#34;对于提供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++-5g++-6clang-3.8编译器使用-std=c++14标志进行了检查。他们都抱怨

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

尽管据我所知,所有使用过的功能都在标准中,因此应予以支持。有趣的是,只要我添加优化标记O1O2O3,编译就会成功。所以我的第二个问题是:如果优化标志处于活动状态,编译器是否只进行编译时计算?我原以为至少会被声明为编译时常量的东西总是推断出来了!

我的问题的最后一部分涵盖了NVIDIA nvcc编译器(版本8.0)。由于我只能将-std=c++11传递给它,因此通常不会涵盖某些功能。但是,使用上面的一个主机编译器,它会抱怨

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

即使通过优化标志!这显然是与上面相同的问题,但是虽然上面的问题更具有学术性(因为我可以简单地使用优化标志来摆脱问题),这里真的是一个问题(关于我不知道的事实) ,当我使用上面提到的变通方法时,在编译时完成了什么 - 这也是更加丑陋的。所以我的第三个问题是:有没有办法在设备代码中使用优化?

以下代码是纯主机和nvcc编译器的MWE:

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

提前致谢!

1 个答案:

答案 0 :(得分:4)

这之间的区别:

doSomething<Container<T>>( ONE );

与这两者相反:

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

在第一种情况下,您将引用直接绑定到ONE,而将其他引用直接绑定到ONE。更具体地说,在第一种情况下你是 odr-using ONE,而不是其他两种情况。当您使用实体时,它需要一个定义,并且template<typename T> class Tester { public: // declaration static constexpr T ONE = CONST<T>::ONE(); // .. }; // definition template <typename T> constexpr T Tester<T>::ONE; 当前已声明但未定义。

您需要定义它:

<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="256" height="256">
  <defs>
    <linearGradient id="magred" x1="0%" y1="0%" x2="0%" y2="100%">
        <stop offset="0%" stop-color="#FF00FF"></stop>
        <stop offset="100%" stop-color="#FF0000"></stop>
    </linearGradient>
    <linearGradient id="magblue" x1="0%" y1="0%" x2="100%" y2="0%">
        <stop offset="0%" stop-color="#FF00FF"></stop>
        <stop offset="100%" stop-color="#0000FF"></stop>
    </linearGradient>
    <linearGradient id="magredmaskgrad" x1="0%" y1="0%" x2="100%" y2="0%">
        <stop offset="0%" stop-color="#FFFFFF"></stop>
        <stop offset="100%" stop-color="#000000"></stop>
    </linearGradient>
    <linearGradient id="magbluemaskgrad" x1="0%" y1="0%" x2="0%" y2="100%">
        <stop offset="0%" stop-color="#FFFFFF"></stop>
        <stop offset="100%" stop-color="#000000"></stop>
    </linearGradient>
    <mask id="magredmask">
        <rect x=0 y=0 width=100% height=100% fill="url(#magredmaskgrad)"/>
    </mask>
    <mask id="magbluemask">
        <rect x=0 y=0 width=100% height=100% fill="url(#magbluemaskgrad)"/>
    </mask>
</defs>
<rect x=0 y=0 width=100% height=100% fill="black"/>
<rect x=0 y=0 width=100% height=100% fill="url(#magred)"
      mask="url(#magredmask)"/>
<rect x=0 y=0 width=100% height=100% fill="url(#magblue)"
      mask="url(#magbluemask)"/>
</svg>