防止模板强制方法中的隐式转换

时间:2014-11-05 22:53:49

标签: c++ templates interface implicit-conversion

假设我们有类似的东西:

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(),该方法采用某种整数类型的参数。问题在于此处允许B::Bar()的所有3个版本。有没有办法只允许这些方法的一个版本,例如,只有在B提供Bar(float)时才编译?

1 个答案:

答案 0 :(得分:1)

您可以使用这种(可怕的)技术,如果使用没有公共A成员的类型B实例化void Foo(float),则会导致编译失败从中获取特定的指针指向成员类型。

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.5是一个double字面值,而不是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