为什么结构不能作为模板非类型参数传递?

时间:2013-04-09 08:08:32

标签: c++ templates c++11

非类型模板参数显然是不是类型的参数,例如:

template<int x>
void foo() { cout << x; }

在这种情况下,除int之外还有其他选项,我想提及this great answer

现在,有一件事让我烦恼:结构。考虑:

struct Triple { int x, y, z; };

Triple t { 1, 2, 3 };

template<Triple const& t>
class Foo { };

现在,使用普通的非类型引用语义,我们可以写:

Foo<t> f;

值得注意的是,t 不能constexpr甚至const,因为这意味着内部联系,这基本上意味着行不会编译。我们可以通过将t声明为const extern来绕过这一点。这本身可能有点奇怪,但真正让我想知道的是为什么这是不可能的:

Foo<Triple { 1, 2, 3 }> f;

我们从编译器得到了一个非常不错的错误:

  

错误:Triple{1, 2, 3}不是类型const Triple&的有效模板参数,因为它不是左值。

我们无法在模板中按值指定Triple,因为这是不允许的。但是,我无法理解那小行代码的真正的问题。不允许使用结构作为值参数的原因是什么。如果我可以使用三个int,为什么不使用三个整数的结构?如果它只有普通的特殊成员,那么它在处理方面不应该只是三个变量。

3 个答案:

答案 0 :(得分:12)

我认为“因为mikta的痛苦才能正确指定并实施”是我们没有这个的主要原因。当然,很容易让这一点工作,但人们会抱怨使用结构模板参数在其他模板参数所做的所有相同情况下都不起作用(考虑部分特化,或者如何处理{{1 }})。

在我看来,整个蛋糕太乱了,只得到一小块就不够令人满意了,可能更令人沮丧。只做这个小小的工作并不会给我提供比以下更多的能力,它具有开箱即用的各种东西(包括部分特化)的额外优势。

operator==

答案 1 :(得分:11)

个用户的更新答案:

C ++ 20添加了对类文字(具有constexpr构造函数的类)非类型模板参数的支持,只要模板参数被值接受,这将允许原始问题中的示例起作用: / p>

template<Triple t> // Note: accepts t by value
class Foo { };

// Works with unnamed instantiation of Triple.
Foo<Triple { 1, 2, 3 }> f1 {};

// Also works if provided from a constexpr variable.
constexpr Triple t { 1, 2, 3 };
Foo<t> f2 {};

此外,整个程序中Triple { 1, 2, 3 }的所有模板参数实例将引用 相同 静态存储持续时间对象:

template<Triple t1, Triple t2>
void Func() {
    assert(&t1 == &t2); // Passes.
}

constexpr Triple t { 1, 2, 3 };

int main()
{
    Func<t, Triple {1, 2, 3}>();
}

来自cppreference

命名为类类型T的非类型模板参数的标识符表示类型为const T的静态存储持续时间对象,称为模板参数对象,其值是将其转换为变量后相应模板参数的值。模板参数的类型。 相同类型的程序中具有相同值的所有此类模板参数表示相同的模板参数对象。

请注意,模板参数允许的类文字类型有很多限制。有关更多详细信息,请查看我写的这篇博客文章,其中解释了文字类NTTP的用法和限制:Literal Classes as Non-type Template Parameters in C++20

答案 2 :(得分:7)

您可以将t定义为const extern,并为其提供外部链接。然后构造工作:

struct Triple { int x, y, z; };

const extern Triple t { 1, 2, 3 };

template<Triple const& t>
class Foo { };

Foo<t> f;

Live example

您无法将临时值传递给参考模板参数的原因是该参数是引用。如果模板参数为const int&,则会出现相同的错误你试图通过7Example

修改

三个int和一个包含三个int的结构之间的区别在于int类型的所有文字都是相同的值(7的所有出现都是template <Triple t> struct Foo {}; Foo<Triple {1, 2, 3}> f1; Foo<Triple {1, 2, 3}> f2; 只有七个),而每个构造函数调用一个结构在概念上创建一个新的实例。拿这个假设的例子:

{{1}}

我认为这会引入额外的复杂性,将这两个构造函数调用“匹配”到同一个模板实例中。