下面的问题是从一个更大的代码中浓缩出来的。因此有些表达式看似矫枉过正或不必要,但对原始代码至关重要。
考虑有一个结构,它包含编译时常量和一个简单的容器类:
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
和 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;
}
提前致谢!
最佳答案
这两者的区别:
doSomething<Container<T>>( ONE );
相对于这两个:
doSomething<Container<T>>( static_cast<T>( ONE ) );
doSomething<Container<T>>( CONST<T>::ONE() );
是在第一种情况下,您将引用直接绑定(bind)到 ONE
,而在其他情况下则不是。更具体地说,您在第一种情况下odr-usingONE
,但在其他两种情况下不是。当您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;
关于c++ - 编译时的模板和 constexpr 推导取决于编译器和优化标志,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39642434/