如果函数模板已经推导出返回类型,有没有办法在不实例化定义的情况下调用它?

Bri*_*ian 5 c++ templates language-lawyer

考虑一些函数模板,例如:

template <class T>
const auto& foo() { static T t; return t; }
Run Code Online (Sandbox Code Playgroud)

如果T是 ,则定义将无效void。尽管如此,我们可以单独实例化声明而不触发错误:

extern template const auto& foo<void>();  // explicit instantiation declaration
Run Code Online (Sandbox Code Playgroud)

现在让我们考虑foo被调用而不是被显式实例化的情况。显然,如果foo在评估的上下文中被调用,特化的定义将被实例化。在未评估的情况下呢?我们知道,如果在未计算的上下文中调用具有非推导返回类型的函数模板,则不会实例化特化的定义。一个明显的例子是std::declval<T>. 尚不清楚对于具有推导返回类型的函数是否可能相同。

例如,我考虑过这个:

static_assert(sizeof( (void)foo<void>(), char{} ) == 1);
Run Code Online (Sandbox Code Playgroud)

然而,即使在这种情况下,编译器肯定有足够的信息来评估sizeof表达式而不知道返回类型,编译错误仍然会发生(godbolt link)。

  • foo<void>在这种情况下,标准的哪些规定需要实例化的定义?
  • 有什么方法foo<void>可以在不会实例化其定义的未计算表达式中调用吗?

dfr*_*fri 3

\n

标准的哪些条款要求在这种情况下实例化 的定义foo<void>

\n
\n

尽管不规范,但[dcl.spec.auto]/11中的注释确实提到任何特化(在其声明的返回类型中带有占位符的函数模板)的使用都将导致隐式实例化 [摘录,强调我的] :

\n
\n

[...] [\xe2\x80\x89注意:因此,任何使用函数模板的特化都会导致隐式实例化。

\n
\n

而且,[dcl.spec.auto]/14涵盖了允许显式实例化声明而不触发实例化的特殊情况,同时可以说还暗示为确定函数模板的返回类型而触发的实例化机制在某种程度上与“常规”实例化机制[重点是我的]:

\n
\n

显式实例化声明不会导致使用占位符类型声明的实体的实例化,但它也不会阻止根据需要实例化该实体以确定其类型。[\xe2\x80\x89示例:

\n
template <typename T> auto f(T t) { return t; }\nextern template auto f(int);    // does not instantiate f<int>\nint (*p)(int) = f;              // instantiates f<int> to determine its return type, but an explicit\n                                // instantiation definition is still required somewhere in the program\n
Run Code Online (Sandbox Code Playgroud)\n

\xe2\x80\x89\xe2\x80\x94\xe2\x80\x89结束示例\xe2\x80\x89]

\n
\n

其中示例的注释(非规范)指出这种特殊情况触发的隐式实例化仅用于返回类型推导,并且不会放弃在其他地方显式实例化定义的需要。

\n
\n
\n

有没有什么方法foo<void>可以在未评估的表达式中调用而不会实例化其定义?

\n
\n

鉴于上面的讨论,我会说:不。即使在未评估的表达式中进行调用也将属于(非规范的)“任何使用”的范围。

\n