我可以使用 declval 来构造未使用的 Return 吗?
Can I use declval to Construct an Unused Return?
假设我有一个将要专门化的模板化函数,所以我真的不关心基本实现。我可以这样做吗:
template <typename T>
T dummy() {
assert(false);
return declval<T>();
}
当我尝试在 visual-studio-2017 中执行此操作时,出现链接错误:
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" 编译器错误,说明您正在尝试使用已删除的函数。这也允许您编写重载而不是特化,这是更可取的,因为在重载决策期间不考虑特化。这在你的情况下是不可能的,因为你不带任何参数,但如果你这样做,你应该考虑它。
假设我有一个将要专门化的模板化函数,所以我真的不关心基本实现。我可以这样做吗:
template <typename T>
T dummy() {
assert(false);
return declval<T>();
}
当我尝试在 visual-studio-2017 中执行此操作时,出现链接错误:
unresolved external symbol
char const && __cdecl std::declval<char const >(void)
(??$declval@$$CBD@std@@YA$$QEBDXZ) referenced in functionchar 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" 编译器错误,说明您正在尝试使用已删除的函数。这也允许您编写重载而不是特化,这是更可取的,因为在重载决策期间不考虑特化。这在你的情况下是不可能的,因为你不带任何参数,但如果你这样做,你应该考虑它。