【问题标题】:Prevent implicit conversion in template-enforced method防止模板强制方法中的隐式转换
【发布时间】:2014-11-05 22:53:49
【问题描述】:

假设我们有这样的事情:

template <class B>
class A
{
   void Foo(B& b)
   {
       b.Bar(0.5);
   }
};

class B
{
    void Bar(float) {}
    void Bar(double) {}
    void Bar(int) {}
};

在这段代码中,类型B 必须提供一个方法Bar(),它接受某个整数类型的参数。问题是这里所有 3 个版本的 B::Bar() 都是允许的。有没有办法只允许这些方法的一个版本,例如,只有在B 提供Bar(float) 时才编译?

【问题讨论】:

  • 为什么B本身没有模板?
  • 所以,为了弄清楚,您想要签入 A 吗?在我看来,您要求的是两种不同的东西 - 可以检查模板参数 B 是否有一个采用浮点数的方法 Bar。但这本身不会阻止像b.Bar(0); 这样的事情。你能澄清一下吗?
  • 您将文字值传递给 Bar 调用,我看不出您试图让编译器为您做您自己无法做到的事情。跨度>

标签: c++ templates interface implicit-conversion


【解决方案1】:

您可以使用这种(可怕的)技术,如果A 使用没有公共void Foo(float) 成员的类型B 实例化,则会导致编译失败,方法是尝试获取特定的指针-来自它的成员类型。

template <class B>
class A
{
public:
   void Foo(B& b)
   {
       static_cast<void (B::*)(float)>(&B::Bar);

       b.Bar(0.5);
   }
};

(Demo of a resulting compilation failure.)

如果你想实际调用这个方法,那么你需要使用b.Bar(0.5f);0.5double 文字,而不是 float 文字,因此您将检查以确保它具有正确的成员,但如果它具有 void Bar(double) 则无论如何您都会调用它。将常量更改为 0.5f 可以解决此问题。

请注意,由于获取指针没有副作用并且不使用结果,因此任何体面的编译器都会将其优化为无操作。


你也可以像这样走传统的 SFINAE 路线:

template <typename T, typename TMethod>
class has_bar_method
{
private:
    struct yes { char _; };
    struct no { char _[2]; };
    template <typename U, TMethod = &U::Bar>
    static yes impl(U*);
    static no impl(...);

public:
    enum { value = sizeof(impl(static_cast<T*>(nullptr))) == sizeof(yes) };
};

这样使用:

void Foo(T& b)
{
    static_assert(has_bar_method<T, void (T::*)(float)>::value, 
                  "T has method void Bar(float)");

    b.Bar(0.5f);
}

现在,如果模板无法实例化,我们会收到一条很好的消息来解释原因:

prog.cpp:25:8: 错误:静态断言失败:T 有方法 void Bar(float)

(Demo)

【讨论】:

  • 标记为答案。你能评论一下为什么你的第一个建议很糟糕吗?
  • @user1715925 就其本身而言,演员表看起来没有必要,并且可能会在以后的维护过程中意外移除。当然,您可以通过添加注释来解释它存在的原因来防止这种情况。但是,它产生的错误消息不是很有帮助。 static_assert 可让您指定更可能清楚地传达问题的自定义错误消息。
  • 谢谢,你介意把它放在答案中吗?它可能会帮助其他人
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多