我有一个std::variants,包含有不同接口的对象。目标是调用一些方法,如果变体中的对象有它的话.我正试图做几个模板装饰器,并寻找一种方法,以较少的模板和没有宏的方式来实现它。最后,我的想法看起来像。
class Good
{
public:
void a(int);
float b(float);
int c(double);
};
class Bad
{
public:
void a(int);
int c(double);
};
template<class T>
class View : protected T
{
public:
using T::a;
using T::b;
};
template<class T, template<class> class V>
constexpr bool IsFitToView = // ??
//usage
std::variant<Good, Bad> variant = //;
std::visit([](auto&& obj) {
if constexpr(IsFitToView<std::decay_t<decltype(obj)>, View>)
{
View view{obj}; // create view from obj
//here work with obj as view, calling a `a` and `b` methods;
}
}, variant);
主要问题是如何创建IsFitToView检查。我的方法是。
template<class T, template<class> class V, class = void>
struct IsFit : std::false_type
{};
template<class T, template<class> class V>
struct IsFit<T, V, std::void_t<V<T>>> : std::true_type
{};
template<class T, template<class> class V>
constexpr bool IsFitToView = IsFit<T, V>::value;
我希望它能像SFINAE那样工作。View<Good>
编译和选择模板专用化 View<Bad>
不能编纂,因为 using T::b;
在...中 View
...但它对好类型和坏类型都返回了真值!我知道我可以通过检查方法的存在来检查它,像这样检查。
std::cout << IsFitToView<Good, View> << IsFitToView<Bad, View>;
我知道我可以通过逐个检查来检查方法的存在性,比如说
if constexpr(HasAFunc<T> && HasBFunc<T> && ...
但我必须创建许多不同的 Views
. 它非常啰嗦,而且很难读懂.请你解释一下为什么我的方法行不通,并给出任何想法来做我想要的事情。谢谢!我有一个std::variants,其中包含有不同接口的对象。
你能解释一下为什么我的方法不能用吗?
你目前的方法与 using
声明失败,因为类的(View
的)主体实例化发生在 即时语境,这意味着它不能导致软错误,从而导致回落至 IsFit
主模板,因此它的特殊化总是能产生更好的匹配,从而导致 IsFitToView
始终 true
.
即使这工作。using T::a;
不会告诉你任何关于 a
. 它可以是一个函数,一个重载的函数集,一个 static
或非static
数据成员,甚至是某个类型的别名,而这个类型恰好存在于 T
的范围。
你能不能给点想法来做我想要的事情?
知道了如何检查函数的存在性,你可以将视图定义为变量模板,将一般谓词分组,例如:。
template <typename T>
inline constexpr bool ViewAB = HasAFunc<T> && HasBFunc<T>;
有了这个,你就可以检查了。
if constexpr (ViewAB<std::decay_t<decltype(obj)>>)
另一个解决方案是使用... 侦测成语 表库基础知识v2。
template <typename T>
using ViewAB = decltype(std::declval<T>().a(3), std::declval<T>().b(3.14f));
template <typename T>
using ViewC = decltype(std::declval<T>().c(2.7271));
并像这样使用它。
if constexpr (std::experimental::is_detected_v<ViewAB, decltype(obj)>)
或者,在 c++20,你可以将视图定义为概念。
template <typename T>
concept ViewAB = requires (T t)
{
t.a(1);
t.b(3.14f);
};
它不仅使代码更容易阅读,清晰地展示了需求与示例用法,而且还产生了一个错误信息,解释哪个约束没有被满足。