是否可以在派生类中禁用Foo()
覆盖(通过std::enable_if
或某些增强魔法),如果T不是某种类型,则无需编写class Derived
的模板专精化?
奖励积分:如果T没有定义某种方法,是否可以禁用覆盖?
这是我的SSCCE:
#include <iostream>
#include <string>
class Base
{
public:
virtual std::string Foo()
{
return "Base";
}
};
template <typename T>
class Derived : public Base
{
public:
virtual std::string Foo() override
{
return "Derived";
}
};
int main()
{
Derived<int> testInt;
std::cout << testInt.Foo() << std::endl;
Derived<float> testFloat;
std::cout << testFloat.Foo() << std::endl;//I would like this to print 'Base'
}
更新
感谢您提供精彩的解决方案,但我无法将其应用于我的真实代码。以下示例应该更好地了解我想要实现的目标:
#include <iostream>
#include <string>
class Object
{
public:
void Test()
{
std::cout << "Test" << std::endl;
}
};
class EmptyObject
{
};
class Base
{
public:
virtual std::string Foo()
{
return "Base";
}
};
template <typename T>
class Derived : public Base
{
public:
virtual std::string Foo() override
{
m_object.Test();
return "Derived";
}
private:
T m_object;
};
int main()
{
Derived<Object> testObject;
std::cout << testObject.Foo() << std::endl;
Derived<EmptyObject> testEmpty;
std::cout << testEmpty.Foo() << std::endl;
}
答案 0 :(得分:1)
而不是模板专门化类,您可以直接模板化该方法:(https://ideone.com/gYwt5r)
template<> std::string Derived<float>::Foo() { return Base::Foo(); }
我只看到类的模板特化,通过向虚拟方法添加T
来禁用将来覆盖,具体取决于final
。
答案 1 :(得分:1)
我会通过创建Derived::Foo
可根据T = float
有条件委托的两个函数来实现此目的。一个将包含真正的Derived::Foo
实现,而另一个将调用Base::Foo
。
template <typename T>
class Derived : public Base
{
public:
virtual std::string Foo() override
{
return do_Foo(std::is_same<T, float>{});
}
private:
std::string do_Foo(std::false_type)
{
return "Derived";
}
std::string do_Foo(std::true_type)
{
return Base::Foo();
}
};
看起来你真正想做的只是在Derived<T>::Foo()
定义某个成员函数时才调用T
实现,否则应该调用Base::Foo()
。这可以使用表达式SFINAE来完成。
template <typename T>
class Derived : public Base
{
public:
std::string Foo() override
{
return do_Foo(true);
}
private:
template<typename U = T>
auto do_Foo(bool)
-> decltype(std::declval<U>().test(), void(), std::string())
{
return "Derived";
}
std::string do_Foo(int)
{
return Base::Foo();
}
};
在上面的代码中,如果类型T
未定义名为test()
的成员函数,则do_Foo(bool)
成员函数模板将不可行。另一方面,如果T::test()
确实存在,那么将选择do_Foo(bool)
,因为do_Foo
传递给Foo
的布尔值使其与{相比}更好地匹配{1}}。
可以在here找到尾随返回类型中do_Foo(int)
表达式内发生的内容的详细说明。
答案 2 :(得分:1)
如果您需要在编译时限制某种类型,则可以将std::enable_if
与std::is_same
一起使用:
typename std::enable_if<std::is_same<T, float>::value, std::string>::type
virtual Foo() override
{
return "Derived";
}
或者,如果模板类型不是您要查找的类型,则可以轻松地将调用重定向到Base
方法,仍然使用std::is_same
:
virtual std::string Foo() override
{
return std::is_same<T, float>::value ? Base::Foo() : "Derived";
}
对于 Bonus ,您可以使用decltype
获取此SO answer的特征,并使用bar()
进行调整,以获取方法template <typename T>
class has_bar
{
typedef char one;
typedef long two;
template <typename C> static one test(decltype(&C::bar) ) ;
template <typename C> static two test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
:
virtual std::string Foo() override
{
return has_bar<T>::value ? "Derived" : Base::Foo() ;
}
限制是您不能对参数或返回类型设置约束。
has_bar
注意:强>
您也可以像我的第一个示例一样使用enable_if
和{{1}},以便在编译时禁用它。
答案 3 :(得分:1)
您可以在层次结构中添加中间类:
class Base
{
public:
virtual std::string Foo()
{
return "Base";
}
};
template <typename T>
class Intermediate : public Base
{
// common operations with m_object
protected: // not private!
T m_object;
};
template <typename T, typename = bool>
class Derived : public Intermediate<T> {};
template <typename T>
class Derived<T, decltype(std::declval<T>().Test(), void(), true)>
: public Intermediate<T>
{
public:
virtual std::string Foo() override
{
this->m_object.Test(); // this-> is necessary here!
return "Derived";
}
};