以下问题来自更大的代码。因此,某些表达似乎是一种过度或不必要的,但对原始代码至关重要。
考虑使用包含编译时常量和简单容器类的结构:
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++-5
,g++-6
和clang-3.8
编译器使用-std=c++14
标志进行了检查。他们都抱怨
undefined reference to `Tester<int>::ONE'
尽管据我所知,所有使用过的功能都在标准中,因此应予以支持。有趣的是,只要我添加优化标记O1
,O2
或O3
,编译就会成功。所以我的第二个问题是:如果优化标志处于活动状态,编译器是否只进行编译时计算?我原以为至少会被声明为编译时常量的东西总是推断出来了!
我的问题的最后一部分涵盖了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;
}
提前致谢!
答案 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>