在尝试使用最近的g ++ - 5编译器时,我在一个文件中写下了以下语句:
template<T> T a;
template<> int a = 1;
Run Code Online (Sandbox Code Playgroud)
结果如下:
警告:模板标题太多
a(应为0)
同样有效的是,它并不专门a<int>.例如
template<typename T> T a;
template<> int a = 1;
int main () {
std::cout << a<double> << "\n"; // prints 0; OK
std::cout << a<int> << "\n"; // prints 0! why not 1?
}
Run Code Online (Sandbox Code Playgroud)
这句法的神秘之处是什么?
c++ template-specialization language-lawyer variable-templates c++14
有:
struct Value
{
template<class T>
static constexpr T value{0};
};
Run Code Online (Sandbox Code Playgroud)
(0) 意思号
template<typename TValue>
struct Something
{
void x()
{
static_assert(TValue::template value<int> == 0, "");
}
};
int main() { Something<Value>{}.x(); return 0; }
Run Code Online (Sandbox Code Playgroud)
不用clang ++ 3.6编译.
错误:如果没有模板参数列表,则无法引用变量模板'value'
不使用g ++ 5.2编译.
错误:'template constexpr const T Value :: value'不是函数模板
(1) 理想
用clang ++和g ++编译.
struct Something
{
void x()
{
static_assert(Value::template value<int> == 0, "");
}
};
int main() { Something{}.x(); return 0; }
Run Code Online (Sandbox Code Playgroud)
为什么(0)无法编译?
如果通过模板参数(在本例中)访问变量模板,似乎会出现问题 …
例如:
class example{
public:
template <class T> static constexpr T var = T(1.5);
};
int main(){
int a = example::var<int>;
example obj;
int b = obj.var<int>;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
GCC会产生以下错误:
'example::var<T>' is not a function template和'var' is not a member template function
Clang正确编译第一个但是第二个产生错误: cannot refer to member 'var' in 'example' with '.'
根据C++ 14标准(ISO/IEC 14882:2014):
第14节,第1段.
类作用域中的变量模板是静态数据成员模板.
第9.4节,第2段.
可以使用qualified-id表达式X :: s来引用类X的静态成员; 没有必要使用类成员访问语法(5.2.5)来引用静态成员.可以使用类成员访问语法来引用静态成员,在这种情况下,评估对象表达式.
因此,可以以两种方式引用恕我直言,类范围的变量模板(即,静态数据成员模板).它可能是编译器中的错误吗?
我发现试图证明这种行为的唯一理由是第9.4.2节第1段中的这句话:
静态数据成员不是类的子对象的一部分.
但是,上述两个段落仍然有效.此外,我尝试了同样的例子,引用其他静态成员,如变量,函数和函数模板,并且所有这些成员都在GCC和Clang中成功编译.
class example{
public:
static int constexpr variable = 1;
void static function(){ …Run Code Online (Sandbox Code Playgroud) 考虑以下示例:
#include <cstdio>
template <int N>
int fib = fib<N - 1> + fib<N - 2>;
template <> int fib<2> = 1;
template <> int fib<1> = 1;
int main()
{
std::printf("%d %d %d", fib<4>, fib<5>, fib<6>);
}
Run Code Online (Sandbox Code Playgroud)
GCC 7.x、8.x、9.x 和 10.x 都打印出3 5 8.
结果是 Clang 5.x、6.x、7.x、8.x、9.x 和 10.x 都打印出来1 3 4了。
Clang的行为令人惊讶。
我缺少的 C++ 标准中的变量模板实例化、全局变量和递归之间是否存在任何微妙的交互?
或者这是一个长期存在的 Clang 错误?
顺便说一下,标记fib为constexpr解决了这个问题(在 Godbolt.org 上)。
在没有初始化主要参考模板的情况下在C++ 14中声明参考模板是否合法,只要它从未实例化?
template<class T>
const T& ref;
template<>
auto ref<int> = 1;
auto x = ref<int>;
Run Code Online (Sandbox Code Playgroud)
这会在GCC和Clang上产生不同的结果:
$ g++ -std=c++14 -c ref.cpp
$
$ clang -std=c++14 -c ref.cpp
ref.cpp:2:10: error: declaration of reference variable 'ref' requires an
initializer
const T& ref;
^~~
1 error generated.
Run Code Online (Sandbox Code Playgroud)
没有必要初始化主要参考模板,因为在实例化之前,它是模板,而不是参考.
我发现我可以这样做:
template<class T>
const T& ref = "Meaningless initialization with any value of any type";
template<>
auto ref<int> = 1;
auto x = ref<int>;
Run Code Online (Sandbox Code Playgroud)
因为显然GCC和Clang都接受但忽略了参考模板初始化器RHS,只要它是一个有效的表达式并且主参考模板永远不会被实例化.任何类型的任何表达式都满足Clang的初始化要求.
只要主参考模板从未实例化,GCC就不需要初始化程序.这似乎是"在精神上"的正确行为,因为在实际实例化参考模板之前,它不应该需要初始化器.
标准在参考模板上不是100%明确.这是我在变量模板实例化上找不到的东西:
14.7.1
除非已显式实例化或显式专门化变量模板特化,否则在使用特化时隐式实例化变量模板特化.
...
实现不应隐式实例化......不需要实例化的变量模板.
14.7.2
除了内联函数,从其初始值或返回值(7.1.6.4)推导出的类型的声明
const …
以下荒谬的示例无法编译,但是还有其他方法可以将变量模板作为模板模板参数传递吗?
template<typename T>
constexpr auto zero = T{0};
template<typename T, template<typename> auto VariableTemplate>
constexpr auto add_one()
{
return VariableTemplate<T> + T{1};
}
int main()
{
return add_one<int, zero>();
}
Run Code Online (Sandbox Code Playgroud)
我试图constexpr像这样向前声明一个变量模板:
template<typename>
constexpr std::size_t iterator_category_value;
Run Code Online (Sandbox Code Playgroud)
我们的目标是记录每一项专业都应该是,constexpr但我必须承认,我从未检查过它是否合法,而且g ++对它很满意.但是,当我尝试使用clang ++编译此spinnet时,我收到以下错误:
Run Code Online (Sandbox Code Playgroud)error: default initialization of an object of const type 'const std::size_t' (aka 'const unsigned long') constexpr std::size_t iterator_category_value; ^ = 0
错误是有道理的,删除constexpr使它消失,所以这不是一个真正的问题.但是,我现在很好奇:标准是否允许constexpr对变量模板进行此类前向声明,还是非法?g ++和clang ++似乎不同意,我想知道如果需要我应该在哪里提交错误报告.
他们都抱怨一个前向声明的constepxr变量,它不是一个变量模板,所以变量模板上下文似乎是编译器不同意的原因.
假设您有一个元组类型,并且您想要提取其模板参数包以便实例化另一个模板.如果那是一个类型模板,那么我可以有这样的实用程序:
template < typename Tuple, template <typename...> typename What >
struct PutTupleInT;
template < typename... Types, template <typename...> typename What >
struct PutTupleInT<std::tuple<Types...>, What>
{
using Result = What<Types...>;
};
Run Code Online (Sandbox Code Playgroud)
但是,如果所需模板是可变模板呢?虽然template <typename...> typename What是类型模板的"占位符",但是变量模板的"占位符"是什么?
我已经尝试了以下forng-4.0.0(现在唯一支持自动类型的非类型模板参数的编译器),但它失败了.实际上我不确定这是否是C++ 17的正确语法.
template < typename Tuple, template <typename...> auto What >
struct PutTupleInV;
template < typename... Types, template <typename...> auto What >
struct PutTupleInV<std::tuple<Types...>, What>
{
static constexpr auto value = What<Types...>;
};
Run Code Online (Sandbox Code Playgroud) c ++ 14提供了可变模板,它在visual-studio-2017中运行良好,但在lambdas中它们似乎分崩离析.例如:
template <typename T>
const auto PI = std::acos(static_cast<T>(-1));
int main() {
auto func = []() { cout << PI<float> << endl; };
func();
}
Run Code Online (Sandbox Code Playgroud)
在gcc 6.3上输出:
3.14159
在Visual Studio 2017上,此输出:
0.0
随着C++ 14中的变量模板(以及Clang已经支持它们)和标准is_same_v类型特征的提议,我认为能够制作新的类型特征如下:
template<typename T>
constexpr bool is_const_and_volatile{std::is_const_v<T> && std::is_volatile_v<T>};
Run Code Online (Sandbox Code Playgroud)
唉,这会导致错误等同于以下SSCCE(这个包含下面提到的所有内容):
#include <type_traits>
template<typename T>
constexpr bool is_pointer{std::is_pointer<T>::value};
template<typename T>
constexpr bool foo{is_pointer<T>};
int main() {
//foo<int *>;
}
Run Code Online (Sandbox Code Playgroud)
在main评论中,Clang吐出以下内容:
警告:变量
is_pointer<type-parameter-0-0>具有内部链接但未定义
它看起来定义为我的(注意,更改T到int *在foo正常工作).在取消注释行main实例foo给出了这样的(再次,T以int *优良工程):
错误:constexpr变量
foo<int *>必须由常量表达式初始化
但是,foo使用以下旧语法替换会导致两个实例都正常工作:
constexpr bool foo{std::is_pointer<T>::value};
Run Code Online (Sandbox Code Playgroud)
关于变量模板有什么我想念的吗?有没有办法用它们构建新的变量模板,或者我是否被迫使用旧的语法来构建新的模板,并且在将它们用于其他代码时只享受语法糖?