我可以使用 declval 来构造未使用的 Return 吗?

Can I use declval to Construct an Unused Return?

假设我有一个将要专门化的模板化函数,所以我真的不关心基本实现。我可以这样做吗:

template <typename T>
T dummy() {
    assert(false);
    return declval<T>();
}

当我尝试在 中执行此操作时,出现链接错误:

unresolved external symbol char const && __cdecl std::declval<char const >(void) (??$declval@$$CBD@std@@YA$$QEBDXZ) referenced in function char const __cdecl dummy<char const>()

同样,这个函数没有被调用,但我确实保存了一个指向它的指针。我可以改用 return T{} 并进行编译,但即使没有 T 的默认构造函数,我也需要它才能工作。有什么办法可以解决这个问题吗?

实例化 dummy 将使用 ODR std::declval<T>,这是标准不允许的。

请注意,简单地省略 return 语句不是编译错误。如果调用 dummy,它只会导致 UB。由于您确定 dummy 永远不会被调用,因此这不会对您造成任何问题。

但是,也许您想要做的是避免让编译器发出控制到达非 void 函数末尾的警告。毕竟,在非调试构建中,如果您碰巧调用 dummy,函数 将到达函数末尾 ,因为 assert 将消失。在那种情况下,我建议放在 assert:

之后
throw std::logic_error("dummy should not be called");

编译器现在应该看到函数不可能在没有 return 值的情况下到达其主体的末尾,因为它根本无法到达末尾。

如果 dummy 实际上以某种方式被调用,而不是调用 UB,这也使得程序更有可能真正崩溃。

您可以通过不提供函数模板的定义来解决该问题。使用

template <typename T>
T dummy();

template <>
int dummy() { std::cout << "template <> int dummy()"; return 42;}

int main()
{
    dummy<int>();
    dummy<double>();
    return 0;
}

您将收到链接器错误,因为 dummy<double>(); 不存在,但如果您将其注释掉,那么代码将编译,因为确实存在 int 的特化。这意味着您不必担心退回任何东西。


你甚至可以使用

template <typename T>
T dummy() = delete;

而不是不提供定义,因此您不会收到链接器错误,而是会收到 "nice" 编译器错误,说明您正在尝试使用已删除的函数。这也允许您编写重载而不是特化,这是更可取的,因为在重载决策期间不考虑特化。这在你的情况下是不可能的,因为你不带任何参数,但如果你这样做,你应该考虑它。