1

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

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

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

函数中引用的未解析的外部符号char const && __cdecl std::declval<char const >(void)(??$declval@$$CBD@std@@YA$$QEBDXZ)char const __cdecl dummy<char const>()

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

4

2 回答 2

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;

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

于 2019-04-08T17:48:43.600 回答
1

实例化dummy将 odr-use std::declval<T>,这是标准不允许的。

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

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

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

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

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

于 2019-04-08T17:44:50.963 回答