【问题标题】:How can I prevent implicit conversions in a function template?如何防止函数模板中的隐式转换?
【发布时间】:2017-05-16 08:15:41
【问题描述】:

如何定义函数模板以防止隐式转换?

看来我可以防止使用非模板函数但不使用函数模板的隐式转换。

将转发引用函数模板定义为= delete 过于激进,因为它会阻止使用非常量左值引用进行调用。

使用 const 右值参数定义函数模板为 =delete [1] 不阻止隐式转换。

将特定类型的右值重载定义为=delete 可行,但我想用模板来完成。

最小代码示例:

struct A {};

struct B {
  B() = default;

  B(const A&) {}
};

// Delete const rvalue reference.
template <class T>
void t_no_rvalue(const T&&) = delete; // 1

void t_no_rvalue(const B&) {}         // 2


// Delete forwarding reference.
template <class T>
void t_no_fwd_ref(T&&) = delete;     // 3

void t_no_fwd_ref(const B&) {}       // 4


// (non-template) Delete const rvalue reference.
void no_rvalue(const B&&) = delete;  // 5

void no_rvalue(const B&) {}          // 6


int main(int argc, char* argv[]) {
  A a;
  B b;

  // Undesired behaviour, implicit conversion allowed.
  t_no_rvalue(a);   // resolves to 2
  t_no_rvalue(b);   // resolves to 2

  // Undesired behaviour, invocation with non-const reference disallowed.
  t_no_fwd_ref(a);  // resolves to 3
  t_no_fwd_ref(b);  // resolves to 3

  // Desired behaviour.
  no_rvalue(a);     // resolves to 5
  no_rvalue(b);     // resolves to 6
}

我的实际用例是变体的散列,如果散列函数不是专门针对所有变体成分,则将变体子类型隐式转换回类变体类型将导致无限递归。上面的示例代码更清晰。

[1] 在 Why can I prevent implicit conversions for primitives but not user-defined types? 中尝试,但代码示例损坏。

【问题讨论】:

  • 我不清楚你在问什么 - 即为什么 no_rvalue 不能解决你的问题?你提到你想要一个模板,但模板参数是什么?如果您提供 main() 显示更多应该或不应该编译的示例,这可能会有所帮助。
  • 在处理函数方面,不要让自己处理 ICS hullabaloo。你为什么不要求你的用户专门化一个 class-template 而不是重载一个 function。我认为您应该探索使用 class-template 专业化或其他一些机制。想想std::hash 是如何设计的。
  • explicit B(const A&amp;){} ??
  • @M.M. no_rvalue 具有所需的行为,但我不希望用户指定两个非模板函数。我宁愿提供一个已删除的版本,并让用户指定他们将实施的版本。
  • @Praveen 这会停止所有比我想要的更激进的隐式转换。我也可能无法更改类构造函数的显式性质(​​例如 std::variant)。

标签: c++ templates overload-resolution


【解决方案1】:

以下重载将阻止隐式转换:

template <class T>
void no_conversions(T) = delete; // 7

void no_conversions(const B&) {} // 8

并导致:

// Requested behaviour.
no_conversions(a); // resolves to 7
no_conversions(b); // resolves to 8

值重载会使隐式转换的重载集中毒,因为它将完全匹配。

编辑:

template <class T>
void no_conversions(const T&) = delete; // 9

void no_conversions(const B&) {}        // 10

同样有效。

【讨论】:

  • 后一种情况,const T&amp;,更适合中毒,因为它不会导致与可能删除的 T 的复制或移动构造函数无关的错误。
  • 谢谢,很高兴选择其中一种方法。你有一个令人困惑的错误的例子吗?
  • 例如,考虑像struct X { X(X&amp;&amp;) = delete; /* ... */} 这样的类。使用按值版本,GCC 将不必要地抱怨复制构造函数被删除:wandbox.org/permlink/TTSATJZNr53RJUKQ。 Clang 稍微好一点,并将相关错误放在首位(wandbox.org/permlink/TTSATJZNr53RJUKQ),但您仍然会用不相关的东西混淆错误输出。通过使用参考版本,您可以避免完全考虑副本,而只关注您真正要表达的意思。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-19
  • 2023-03-09
  • 1970-01-01
相关资源
最近更新 更多