天道酬勤,学无止境

非类型模板参数(Non-type template parameters)

问题

我知道非类型模板参数应该是一个常数整数表达式。 有人可以解释为什么会这样吗?

template <std::string temp>
void foo()
{
     // ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'. 

我知道什么是常数积分表达式。 不允许使用上述代码段中的非常量类型(例如std::string的原因是什么?

回答1

之所以不能执行此操作,是因为在编译期间无法解析和替换非常数表达式。 它们可能会在运行时更改,这将需要在运行时生成新模板,这是不可能的,因为模板是编译时的概念。

这是标准允许非类型模板参数(14.1 [temp.param] p4)的内容:

非类型模板参数应具有以下(可选的,通过cv限定)类型之一:

  • 整数或枚举类型,
  • 指向对象或函数的指针,
  • 对对象的左值引用或对函数的左值引用,
  • 指向成员的指针,
  • std::nullptr_t
回答2

那是不允许的。

但是,这是允许的:

template <std::string * temp> //pointer to object
void f();

template <std::string & temp> //reference to object
void g();

参见C ++ Standard(2003)中的§14.1/ 6,7,8。


插图:

template <std::string * temp> //pointer to object
void f()
{
   cout << *temp << endl;
}

template <std::string & temp> //reference to object
void g()
{
     cout << temp << endl;
     temp += "...appended some string";
}

std::string s; //must not be local as it must have external linkage!

int main() {
        s = "can assign values locally";
        f<&s>();
        g<s>();
        cout << s << endl;
        return 0;
}

输出:

can assign values locally
can assign values locally
can assign values locally...appended some string
回答3

您需要能够处理模板参数

template <std::string temp>
void f() {
 // ...
}

f<"foo">();
f<"bar">(); // different function!?

现在,一个impl将需要为std::string或就此而言的任何其他任意用户定义的类提出一个唯一的字符序列,该类存储一个特定的值,其含义对于实现而言是未知的。 另外,在编译时无法计算任意类对象的值。

计划考虑允许将文字类类型作为C ++ 0x后的模板参数类型,并通过常量表达式对其进行初始化。 可以通过使数据成员根据其值递归地对其进行处理(例如,对于基类,我们可以应用深度优先,从左到右遍历)。 但这绝对不适用于任意类。

回答4

模板参数列表中提供的非类型模板参数是一个表达式,其值可以在编译时确定。 这样的论点必须是:

常量表达式,具有外部链接的函数或对象的地址或静态类成员的地址。

另外,字符串文字是具有内部链接的对象,因此您不能将它们用作模板参数。 您也不能使用全局指针。 考虑到四舍五入错误的明显可能性,不允许使用浮点文字。

标签

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • static_assert取决于非类型模板参数(gcc和clang的不同行为)(static_assert dependent on non-type template parameter (different behavior on gcc and clang))
    问题 template <int answer> struct Hitchhiker { static_assert(sizeof(answer) != sizeof(answer), "Invalid answer"); }; template <> struct Hitchhiker<42> {}; 在尝试使用static_assert禁用常规模板实例化时,我发现即使在未实例化模板的情况下,上面的clang代码也会生成断言错误,而gcc仅在使用42以外的参数实例化Hitchhiker时才生成断言错误。 摆弄我发现这个断言: template <int answer> struct Hitchhiker { static_assert(sizeof(int[answer]) != sizeof(int[answer]), "Invalid answer"); }; template <> struct Hitchhiker<42> {}; 在两个编译器上的行为都相同:断言仅在实例化常规模板时才起作用。 该标准说明了什么,哪种编译器是正确的? g++ 4.9.2 clang++ 3.50 回答1 @TartainLlama找到的报价 如果由于不依赖模板参数的构造而导致模板定义后的假想实例化不正确,则程序不正确;否则,程序将不正确。 无需诊断。 N4296 [温度] / 8
  • 是否可以模拟模板 ?(Is it possible to emulate template<auto X>?)
    问题 有可能吗? 我希望它能够启用编译时传递参数。 假设它只是为了用户方便,因为总是可以使用template<class T, T X>键入实型,但是对于某些类型(例如,指向成员函数的指针),即使使用decltype作为快捷方式,它也很繁琐。 考虑以下代码: struct Foo{ template<class T, T X> void bar(){ // do something with X, compile-time passed } }; struct Baz{ void bang(){ } }; int main(){ Foo f; f.bar<int,5>(); f.bar<decltype(&Baz::bang),&Baz::bang>(); } 可以将其转换为以下内容吗? struct Foo{ template<auto X> void bar(){ // do something with X, compile-time passed } }; struct Baz{ void bang(){ } }; int main(){ Foo f; f.bar<5>(); f.bar<&Baz::bang>(); } 回答1 更新后:否。 C ++中没有此类功能。 最接近的是宏: #define AUTO_ARG(x) decltype(x), x f.bar<AUTO
  • 为什么必须在何处以及为什么要放置“模板”和“类型名”关键字?(Where and why do I have to put the “template” and “typename” keywords?)
    问题 在模板中,为什么必须在何处以及为什么将typename和template放在从属名称上? 无论如何,从属名称到底是什么? 我有以下代码: template <typename T, typename Tail> // Tail will be a UnionNode too. struct UnionNode : public Tail { // ... template<typename U> struct inUnion { // Q: where to add typename/template here? typedef Tail::inUnion<U> dummy; }; template< > struct inUnion<T> { }; }; template <typename T> // For the last node Tn. struct UnionNode<T, void> { // ... template<typename U> struct inUnion { char fail[ -2 + (sizeof(U)%2) ]; // Cannot be instantiated for any U }; template< > struct inUnion<T> { }; }; 我typedef Tail::inUnion<U>
  • 作为模板参数传递的函数(Function passed as template argument)
    问题 我正在寻找涉及将C ++模板函数作为参数传递的规则。 C ++支持此功能,如此处的示例所示: #include <iostream> void add1(int &v) { v+=1; } void add2(int &v) { v+=2; } template <void (*T)(int &)> void doOperation() { int temp=0; T(temp); std::cout << "Result is " << temp << std::endl; } int main() { doOperation<add1>(); doOperation<add2>(); } 然而,学习这种技术是困难的。 谷歌搜索“作为模板参数的功能”并没有多大帮助。 令人惊讶的是,经典的C ++模板《完整指南》也没有对此进行讨论(至少不是从我的搜索中获得)。 我的问题是这是否有效的C ++(或只是一些广泛支持的扩展)。 另外,有没有办法在这种模板调用期间允许具有相同签名的仿函数与显式函数互换使用? 下列不工作在上面的程序,至少在Visual C ++,因为语法显然是错误的。 能够为函子切换功能是一件很不错的事,反之亦然,就像您要定义自定义比较操作时,可以将函数指针或函子传递给std :: sort算法的方式一样。 struct add3 { void operator()
  • 为什么不能将float值用作模板参数?(Why can't I use float value as a template parameter?)
    问题 当我尝试使用float作为模板参数时,编译器会为该代码哭泣,而int可以正常工作。 是因为我不能使用float作为模板参数吗? #include<iostream> using namespace std; template <class T, T defaultValue> class GenericClass { private: T value; public: GenericClass() { value = defaultValue; } T returnVal() { return value; } }; int main() { GenericClass <int, 10> gcInteger; GenericClass < float, 4.6f> gcFlaot; cout << "\n sum of integer is "<<gcInteger.returnVal(); cout << "\n sum of float is "<<gcFlaot.returnVal(); return 0; } 错误: main.cpp: In function `int main()': main.cpp:25: error: `float' is not a valid type for a template constant parameter main.cpp
  • C ++ 17中自动输入模板参数的优点(Advantages of auto in template parameters in C++17)
    问题 auto (可能)将在C ++ 17中引入的模板参数中的auto有什么优势? 当我要实例化模板代码时,它仅仅是对auto的自然扩展吗? auto v1 = constant<5>; // v1 == 5, decltype(v1) is int auto v2 = constant<true>; // v2 == true, decltype(v2) is bool auto v3 = constant<'a'>; // v3 == 'a', decltype(v3) is char 我可以从该语言功能中学到什么? 回答1 template <auto>功能(P0127R1)在芬兰Oulu的ISO C ++ 2016会议中被C ++接受。 模板参数中的auto关键字可用于指示非类型参数,该非类型参数的类型是在实例化时推导出的。 这有助于将其视为一种更方便的书写方式: template <typename Type, Type value> 例如, template <typename Type, Type value> constexpr Type constant = value; constexpr auto const IntConstant42 = constant<int, 42>; 现在可以写成 template <auto value> constexpr
  • 为什么模板参数推导在这里不起作用?(Why is the template argument deduction not working here?)
    问题 我创建了两个简单的函数,它们获取模板参数和一个定义类型的空结构: //S<T>::type results in T& template <class T> struct S { typedef typename T& type; }; //Example 1: get one parameter by reference and return it by value template <class A> A temp(typename S<A>::type a1) { return a1; } //Example 2: get two parameters by reference, perform the sum and return it template <class A, class B> B temp2(typename S<A>::type a1, B a2)//typename struct S<B>::type a2) { return a1 + a2; } 参数类型应用于结构S以获得引用。 我用一些整数值来调用它们,但是编译器无法推断出参数: int main() { char c=6; int d=7; int res = temp(c); int res2 = temp2(d,7); } 错误1错误C2783:“ A temp(S :: type)”
  • Non-type template parameters
    I understand that the non-type template parameter should be a constant integral expression. Can someone shed light why is it so ? template <std::string temp> void foo() { // ... } error C2993: 'std::string' : illegal type for non-type template parameter 'temp'. I understand what a constant integral expression is. What are the reasons for not allowing non-constant types like std::string as in the above snippet ?
  • SFINAE works differently in cases of type and non-type template parameters
    Why does this code work: template< typename T, std::enable_if_t<std::is_same<T, int>::value, T>* = nullptr> void Add(T) {} template< typename T, std::enable_if_t<!std::is_same<T, int>::value, T>* = nullptr> void Add(T) {} and can correctly distinguish between these two calls: Add(1); Add(1.0); while the following code if compiled results in the redefinition of Add() error? template< typename T, typename = typename std::enable_if<std::is_same<T, int>::value, T>::type> void Add(T) {} template< typename T, typename = typename std::enable_if<!std::is_same<T, int>::value, T>::type> void Add(T) {}
  • (Partially) specializing a non-type template parameter of dependent type
    Maybe I'm tired, but I'm stuck with this simple partial specialization, which doesn't work because non-type template argument specializes a template parameter with dependent type 'T': template <typename T, T N> struct X; template <typename T> struct X <T, 0>; Replacing 0 by T(0), T{0} or (T)0 doesn't help. So is this specialization even possible?
  • static_assert dependent on non-type template parameter (different behavior on gcc and clang)
    template <int answer> struct Hitchhiker { static_assert(sizeof(answer) != sizeof(answer), "Invalid answer"); }; template <> struct Hitchhiker<42> {}; While trying to disable general template instantiation with static_assert I discovered that the above code in clang generates the assert error even when the template is not instantiated, while gcc generates the assert error only when instantiating Hitchhiker with a parameter other than 42. Fiddling around I found that this assert: template <int answer> struct Hitchhiker { static_assert(sizeof(int[answer]) != sizeof(int[answer]), "Invalid answer")
  • 通用Lambda如何在C ++ 14中工作?(How does generic lambda work in C++14?)
    问题 通用lambda如何在C ++ 14标准中工作(将auto关键字作为参数类型)? 它是否基于C ++模板,其中每个不同的参数类型,编译器都会生成具有相同主体但被替换类型的新函数(编译时多态性)?或者它更类似于Java的泛型(类型擦除)? 代码示例: auto glambda = [](auto a) { return a; }; 回答1 通用lambda在C++14中引入。 简单来说,由lambda表达式定义的闭包类型将具有模板化调用运算符,而不是C++11的lambdas的常规非模板调用运算符(当然,当auto在参数列表中至少出现一次时)。 所以你的例子: auto glambda = [] (auto a) { return a; }; 将glambda这种类型的实例: class /* unnamed */ { public: template<typename T> T operator () (T a) const { return a; } }; C ++ 14标准草案n3690的5.1.2 / 5段指定了如何定义给定lambda表达式的闭包类型的调用运算符: 非泛型lambda表达式的闭包类型具有公共内联函数调用运算符(13.5.4),其参数和返回类型分别由lambda表达式的parameter-declaration-clause和Trailing
  • 在C ++ 11中is_constexpr是否可能?(Is is_constexpr possible in C++11?)
    问题 是否可以基于C ++ 11表达式是否为C ++ 11中的常量表达式(即constexpr )来生成编译时布尔值? 关于SO的几个问题与此相关,但是我在任何地方都看不到一个直接的答案。 回答1 截至2017年,在C ++ 11中不可能使用is_constexpr 。 这听起来有点奇怪,所以让我解释一下历史。 首先,我们添加了此功能来解决缺陷:http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#1129 Johannes Schaub-litb发布了constexpr检测宏,该宏依赖于常量表达式隐式为noexcept的规定。 这在C ++ 11中有效,但至少从未被某些编译器(例如clang)实现。 然后,作为C ++ 17的一部分,我们评估了从C ++ 17中删除不赞成使用的异常规范。 作为该措辞的副作用,我们无意中删除了该规定。 当核心工作组讨论重新添加该条款时,他们意识到这样做存在一些严重的问题。 您可以在LLVM错误报告中查看完整的详细信息。 因此,我们决定不考虑将其重新添加到所有版本的标准中,而是将其删除,而不是重新添加。 据我所知,这样做的结果是无法检测一个表达式是否可用作常量表达式。 回答2 我曾经写过它(编辑:请参见下面的限制和说明)。 来自https://stackoverflow.com/a
  • 与定义的类具有相同类型的静态constexpr成员(static constexpr member of same type as class being defined)
    问题 我希望类C具有类型C的静态constexpr成员。这在C ++ 11中可能吗? 尝试1: struct Foo { constexpr Foo() {} static constexpr Foo f = Foo(); }; constexpr Foo Foo::f; g ++ 4.7.0表示:“无效使用不完整类型”指的是Foo()调用。 尝试2: struct Foo { constexpr Foo() {} static constexpr Foo f; }; constexpr Foo Foo::f = Foo(); 现在的问题是,在类定义中缺少constexpr成员f的初始化程序。 尝试3: struct Foo { constexpr Foo() {} static const Foo f; }; constexpr Foo Foo::f = Foo(); 现在,g ++抱怨在constexpr重新声明了Foo::f 。 回答1 如果我正确地解释了标准,那是不可能的。 (第9.4.2.2/3节)[...]可以使用constexpr说明符在类定义中声明文字类型的静态数据成员。 如果是这样,则其声明应指定一个括号等于相等的初始化程序,其中作为赋值表达式的每个初始化程序子句都是一个常量表达式。 [...] 从上面的内容
  • 将'typedef'从基础类传播到'template'的派生类(Propagating 'typedef' from based to derived class for 'template')
    问题 我正在尝试定义基类,其中仅包含typedef。 template<typename T> class A { public: typedef std::vector<T> Vec_t; }; template<typename T> class B : public A<T> { private: Vec_t v; // fails - Vec_t is not recognized }; 为什么在BI中收到无法识别Vec_t的错误,我需要显式地编写它? typename A<T>::Vec_t v; 回答1 我相信这个问题是重复的,但我现在找不到。 C ++ Standard表示您应根据14.6.2 / 3完全限定名称: 在类模板或类模板的成员的定义中,如果类模板的基类取决于template-parameter,则在类定义时,在不限定名称的查找过程中都不会检查基类作用域模板或成员,或在类模板或成员的实例化期间。 UPD:我终于发现重复了:在这里。 回答2 在模板的情况下,有一些叫做依赖和非依赖的名称。 如果名称依赖于模板参数T,则其依赖名称以及不依赖参数T的其他名称均为独立名称。 这是规则:编译器在查找非依赖名称(例如Vec_t)时不查找依赖基类(例如A)。 结果,编译器甚至不知道它们是否存在,更不用说类型了。 直到知道T ,编译器才能假定Vec_t是类型,因为存在A<T
  • 使用'class'或'typename'作为模板参数? [复制](Use 'class' or 'typename' for template parameters? [duplicate])
    问题 这个问题已经在这里有了答案: 8年前关闭。 可能重复: 模板中关键字“类型名”和“类”的C ++区别 在C ++中定义功能模板或类模板时,可以这样写: template <class T> ... 或者可以这样写: template <typename T> ... 有充分的理由优先选择一个吗? 我接受了最受欢迎(且最有趣)的答案,但真正的答案似乎是“不,没有充分的理由偏爱一个。” 它们是等效的(如下所述除外)。 有些人有理由总是使用typename 。 有些人有理由总是使用class 。 有些人有理由同时使用两者。 有些人不在乎使用哪个。 但是请注意,在此之前,C ++中的模板模板参数,使用的情况下,17 class ,而不是typename是必需的。 请参阅下面的user1428839的答案。 (但是这种特殊情况不是优先考虑的问题,这是语言的要求。) 回答1 斯坦·李普曼(Stan Lippman)在这里谈到了这一点。 我认为这很有趣。 摘要:Stroustrup最初使用class在模板中指定类型,以避免引入新的关键字。 委员会中的一些人担心关键字的这种重载会导致混乱。 后来,委员会引入了一个新的关键字typename来解决语法歧义,并决定让它也用于指定模板类型,以减少混淆,但是为了向后兼容, class保留了其重载的含义。 回答2 根据斯科特·迈尔斯(Scott
  • 当我们有模板模板参数时,为什么需要allocator :: rebind?(Why is allocator::rebind necessary when we have template template parameters?)
    问题 每个分配器类都必须具有类似于以下内容的接口: template<class T> class allocator { ... template<class Other> struct rebind { typedef allocator<Other> other; }; }; 使用分配器的类这样做是多余的: template<class T, class Alloc = std::allocator<T> > class vector { ... }; 但是为什么这是必要的呢? 换句话说,他们不能说: template<class T> class allocator { ... }; template<class T, template<class> class Alloc = std::allocator> class vector { ... }; 哪一种既更优雅,更少冗余,又(在某些类似情况下)可能更安全? 他们为什么要走rebind路线,这也会导致更多的冗余(即您必须说两次T )? (类似的问题出现在char_traits和其余的问题上……尽管它们没有全部rebind ,但它们仍然可以从模板模板参数中受益。) 编辑: 但是,如果您需要多个模板参数,则此方法将无效! 实际上,它运作得很好! template<unsigned int PoolSize> struct
  • 模板模板参数有哪些用途?(What are some uses of template template parameters?)
    问题 我已经看到了一些使用模板模板参数(即以模板为参数的模板)进行基于策略的类设计的C ++示例。 这种技术还有什么其他用途? 回答1 我认为您需要使用模板模板语法来传递参数,该参数的类型是依赖于另一个模板的模板,如下所示: template <template<class> class H, class S> void f(const H<S> &value) { } 在这里, H是模板,但是我希望此函数处理H所有专业化问题。 注意:我从事c ++编程已经很多年了,只需要这样做一次。 我发现它是很少需要的功能(在需要时当然很方便!)。 我一直在尝试考虑好的例子,说实话,大多数情况下这不是必需的,但让我们来设计一个例子。 让我们假设std::vector没有typedef value_type 。 那么,您将如何编写一个函数来为vectors元素创建正确类型的变量呢? 这会起作用。 template <template<class, class> class V, class T, class A> void f(V<T, A> &v) { // This can be "typename V<T, A>::value_type", // but we are pretending we don't have it T temp = v.back(); v.pop_back();
  • SFINAE以返回类型工作,但不作为模板参数(SFINAE working in return type but not as template parameter)
    问题 I already used the SFINAE idiom quite a few times and I got used to put my std::enable_if<> in template parameters rather than in return types. However, I came across some trivial case where it didn't work, and I'm not sure why. First of all, here is my main: int main() { foo(5); foo(3.4); } Here is an implementation of foo that triggers the error: template<typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type> auto foo(T) -> void { std::cout << "I'm an integer!\n"; } template<typename T, typename = typename std::enable_if<std::is_floating_point<T>::value>::type>
  • C ++模板构造函数(C++ template constructor)
    问题 我希望有一个带有模板构造函数且没有参数的非模板类。 据我了解,这是不可能的(因为它会与默认构造函数发生冲突-是吗? ),解决方法如下: class A{ template <typename U> A(U* dummy) { // Do something } }; 也许对此有更好的选择(或更好的解决方法)? 回答1 调用构造函数模板时,无法显式指定模板参数,因此必须通过参数推导来推导它们。 这是因为如果您说: Foo<int> f = Foo<int>(); <int>是类型Foo的模板参数列表,而不是其构造函数。 构造函数模板的参数列表无处可去。 即使有了解决方法,您仍然必须传递参数才能调用该构造函数模板。 尚不清楚您要实现的目标。 回答2 您可以创建模板化的工厂功能: class Foo { public: template <class T> static Foo* create() // could also return by value, or a smart pointer { return new Foo(...); } ... }; 回答3 据我了解,这是不可能的(因为它会与默认构造函数发生冲突-是吗?) 你错了。 它不会以任何方式发生冲突。 您永远无法调用它。 回答4 template<class...>struct types{using type