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

Prevent implicit conversion in template-enforced method

本文关键字:转换 方法      更新时间:2023-10-16

假设我们有这样的东西:

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()。有没有办法只允许这些方法的一个版本,例如,如果B提供Bar(float),则只进行编译?

您可以使用这种(可怕的)技术,如果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);
   }
};

(导致编译失败的演示。)

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

请注意,由于获取指针没有任何副作用,并且不会使用结果,因此任何优秀的编译器都会将其作为no-op进行优化。


你也可以走传统的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)

(演示)