2014-11-05 77 views
0

假设我们有这样的事情:模板强制执行的方法防止隐式转换

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)时才能编译?

+0

为什么B本身没有模板? – NaCl 2014-11-05 23:01:41

+0

那么,为了使事情顺利,你想要在A?在我看来,你需要两种不同的东西 - 可以检查模板参数'B'是否有一个带有浮点的方法'Bar'。但是,这本身不会阻止像'b.Bar(0);'这样的东西。你能澄清吗? – jrok 2014-11-05 23:08:16

+0

你正在将字面值传递给'Bar'调用,我没有看到你想让编译器为你做的事情,你不能自己做。 – Aesthete 2014-11-05 23:14:15

回答

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: error: static assertion failed: T has method void Bar(float)

Demo

+0

标记为答案。你可以评论为什么你的第一个建议是可怕的吗? – user1715925 2014-11-06 04:16:36

+1

@ user1715925本身,铸件看起来没有必要,在将来的维护过程中可能会被意外移除。当然,你可以通过添加注释来解释它为什么会存在。但是,它产生的错误信息不是很有帮助。 'static_assert'可以让你指定一个自定义的错误消息,它更可能清楚地表达问题。 – cdhowie 2014-11-06 04:27:08

+0

谢谢,你是否介意在答案中提出这个问题?它可能会帮助其他人 – user1715925 2014-11-06 05:09:56