我可以使用declval构造未使用的退货吗?

时间:2019-04-08 17:35:18

标签: c++ visual-studio-2017 return-value linker-errors declval

假设我有一个将要专门化的模板化函数,因此我真的不在乎基本实现。我可以做这样的事情吗?

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

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

  在功能char const && __cdecl std::declval<char const >(void)中引用的

未解析的外部符号char const __cdecl dummy<char const>()(?? $ declval @ $$ CBD @ std @@ YA $$ QEBDXZ)

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

2 个答案:

答案 0 :(得分:3)

您可以通过不提供功能模板的定义来解决此问题。使用

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;

与其提供一个定义,而不是提供一个链接器错误,您将得到一个“不错的”编译器错误,表明您正在尝试使用已删除的函数,而不是提供一个定义。这也允许您编写重载而不是专门化,这是可取的,因为在解决重载时不考虑专门化。对于您而言,这实际上是不可能的,因为您不带任何参数,但如果您这样做,则应考虑使用它。

答案 1 :(得分:1)

实例化dummy会滥用std::declval<T>,这是标准所不允许的。

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

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

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

现在,编译器应该看到该函数不可能不返回值而到达其主体结尾,因为它根本无法到达结尾。

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