基于方法专门化模板 最近我在java中编程很多,现在我回到C++的根(我真的开始丢失指针和分段错误)。知道C++对模板有广泛的支持,我想知道它是否具有java的一些能力,这对于编写通用代码可能是有用的。假设我有两组课。其中一个有first()方法,另一个有second()方法。根据一个类所拥有的方法,有没有一种方法可以专门化编译器选择的模板?我的目标是类似于Java的行为: public class Main { public static void main(String[] args) { First first = () -> System.out.println("first"); Second second = () -> System.out.println("second"); method(first); method(second); } static <T extends First> void method(T argument) { argument.first(); } static <T extends Second> void method(T argument) { argument.second(); } }
所以我的问题是:通过专门化每一个案例,在不创建不必要的boileplate代码的情况下实现这样的行为是可能的吗?您将使用我们称之为sfinae的东西,而不是基于方法专门化模板 最近我在java中编程很多,现在我回到C++的根(我真的开始丢失指针和分段错误)。知道C++对模板有广泛的支持,我想知道它是否具有java的一些能力,这对于编写通用代码可能是有用的。假设我有两组课。其中一个有first()方法,另一个有second()方法。根据一个类所拥有的方法,有没有一种方法可以专门化编译器选择的模板?我的目标是类似于Java的行为: public class Main { public static void main(String[] args) { First first = () -> System.out.println("first"); Second second = () -> System.out.println("second"); method(first); method(second); } static <T extends First> void method(T argument) { argument.first(); } static <T extends Second> void method(T argument) { argument.second(); } },c++,templates,generics,duck-typing,C++,Templates,Generics,Duck Typing,所以我的问题是:通过专门化每一个案例,在不创建不必要的boileplate代码的情况下实现这样的行为是可能的吗?您将使用我们称之为sfinae的东西,而不是。这是一种基于参数类型在函数上添加常量的技术 下面是您在c++中的实现方法: template <typename T> auto generic_fcn(T argument) -> void_t<decltype(argument.first())> { argument.first(); } te
。这是一种基于参数类型在函数上添加常量的技术
下面是您在c++中的实现方法:
template <typename T>
auto generic_fcn(T argument) -> void_t<decltype(argument.first())> {
argument.first();
}
template <typename T>
auto generic_fcn(T argument) -> void_t<decltype(argument.second())> {
argument.second();
}
另一件大事是,如果你有这样的课程:
struct Bummer {
void first() {}
void second() {}
};
然后编译器将有效地告诉您,调用是不明确的,因为类型匹配这两个约束
如果您真的想测试一个类型是否扩展了另一个(或者在C++中实现,它是一样的),您可以使用类型特性<代码>:ST:::
template <typename T>
auto generic_fcn(T argument) -> std::enable_if_t<std::is_base_of<First, T>::value> {
argument.first();
}
template <typename T>
auto generic_fcn(T argument) -> std::enable_if_t<std::is_base_of<Second, T>::value> {
argument.second();
}
模板
自动泛型\u fcn(T参数)->std::启用\u if\u T{
参数first();
}
模板
自动泛型\u fcn(T参数)->std::启用\u if\u T{
第二个参数();
}
要阅读有关此主题的更多信息,请查看CPPreference,您可以查看标准库提供的信息。您可以按如下方式发送呼叫:
#include<utility>
#include<iostream>
struct S {
template<typename T>
auto func(int) -> decltype(std::declval<T>().first(), void())
{ std::cout << "first" << std::endl; }
template<typename T>
auto func(char) -> decltype(std::declval<T>().second(), void())
{ std::cout << "second" << std::endl; }
template<typename T>
auto func() { return func<T>(0); }
};
struct First {
void first() {}
};
struct Second {
void second() {}
};
int main() {
S s;
s.func<First>();
s.func<Second>();
}
#包括
#包括
结构{
模板
自动函数(int)->decltype(std::declval().first(),void())
{STD::CUT< P> C++中有很多选项可用.
我更喜欢自由函数并正确返回任何结果类型
#include <utility>
#include <type_traits>
#include <iostream>
struct X
{
int first() { return 1; }
};
struct Y
{
double second() { return 2.2; }
};
//
// option 1 - specific overloads
//
decltype(auto) generic_function(X& x) { return x.first(); }
decltype(auto) generic_function(Y& y) { return y.second(); }
//
// option 2 - enable_if
//
namespace detail {
template<class T> struct has_member_first
{
template<class U> static auto test(U*p) -> decltype(p->first(), void(), std::true_type());
static auto test(...) -> decltype(std::false_type());
using type = decltype(test(static_cast<T*>(nullptr)));
};
}
template<class T> using has_member_first = typename detail::has_member_first<T>::type;
namespace detail {
template<class T> struct has_member_second
{
template<class U> static auto test(U*p) -> decltype(p->second(), void(), std::true_type());
static auto test(...) -> decltype(std::false_type());
using type = decltype(test(static_cast<T*>(nullptr)));
};
}
template<class T> using has_member_second = typename detail::has_member_second<T>::type;
template<class T, std::enable_if_t<has_member_first<T>::value>* =nullptr>
decltype(auto) generic_func2(T& t)
{
return t.first();
}
template<class T, std::enable_if_t<has_member_second<T>::value>* =nullptr>
decltype(auto) generic_func2(T& t)
{
return t.second();
}
//
// option 3 - SFNAE with simple decltype
//
template<class T>
auto generic_func3(T&t) -> decltype(t.first())
{
return t.first();
}
template<class T>
auto generic_func3(T&t) -> decltype(t.second())
{
return t.second();
}
int main()
{
X x;
Y y;
std::cout << generic_function(x) << std::endl;
std::cout << generic_function(y) << std::endl;
std::cout << generic_func2(x) << std::endl;
std::cout << generic_func2(y) << std::endl;
std::cout << generic_func3(x) << std::endl;
std::cout << generic_func3(y) << std::endl;
}
#包括
#包括
#包括
结构X
{
int first(){return 1;}
};
结构
{
双秒(){return 2.2;}
};
//
//选项1-特定重载
//
decltype(auto)泛型_函数(X&X){return X.first();}
decltype(auto)泛型_函数(Y&Y){return Y.second();}
//
//选项2-如果需要,启用
//
名称空间详细信息{
模板结构首先有\u个成员\u
{
模板静态自动测试(U*p)->decltype(p->first(),void(),std::true_type());
静态自动测试(…)->decltype(std::false_type());
使用type=decltype(test(static_cast(nullptr));
};
}
模板使用has_member_first=typename detail::has_member_first::type;
名称空间详细信息{
模板结构具有\u成员\u秒
{
模板静态自动测试(U*p)->decltype(p->second(),void(),std::true_type());
静态自动测试(…)->decltype(std::false_type());
使用type=decltype(test(static_cast(nullptr));
};
}
模板使用has_member_second=typename detail::has_member_second::type;
模板
decltype(自动)通用功能2(T&T)
{
返回t.first();
}
模板
decltype(自动)通用功能2(T&T)
{
返回t.second();
}
//
//选项3-具有简单decltype的SFNAE
//
模板
自动泛型函数3(T&T)->decltype(T.first())
{
返回t.first();
}
模板
自动泛型函数3(T&T)->decltype(T.second())
{
返回t.second();
}
int main()
{
X;
Y;
std::cout这里有一个小库,可以帮助您确定成员是否存在
namespace details {
template<template<class...>class Z, class always_void, class...>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;
这被称为标记分派
method
首先调用方法
,该方法确定T&
是否可以通过.first()
调用。如果可以,则调用调用.first()的方法。
如果不能,它将调用转发到方法\u second
的方法,并测试它是否有.second()
如果两者都没有,则调用=delete
函数,该函数在编译时生成错误消息
有很多很多方法可以做到这一点。我个人喜欢标记调度,因为你可以从失败中得到比SFIANE生成的更好的错误消息
在C++17中,您可以更直接地:
template<class T>
void method(T & t) {
if constexpr (has_first<T&>{}) {
t.first();
}
if constexpr (has_second<T&>{}) {
t.second();
}
}
模板
空隙法(T&T){
if constexpr(has_first{}){
t、 第一个();
}
if constexpr(有第二个{}){
t、 第二个();
}
}
您可以使用一个策略类作为另一个模板参数,并从中继承。然后,您可以为不同的容器类型提供策略实现。另请参见。您可以给我一个示例,说明如何解决此问题吗?因为这听起来值得一试。wikipedia文章中有一个示例,如果您搜索这些词,请你会找到更多的。好的,我试试。谢谢。
namespace details {
template<template<class...>class Z, class always_void, class...>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z, void, Ts...>;
template<class T>
using first_result = decltype(std::declval<T>().first());
template<class T>
using has_first = can_apply<first_result, T>;
template<class T>
void method_second( T& t, std::true_type has_second ) {
t.second();
}
template<class T>
void method_first( T& t, std::false_type has_first ) = delete; // error message
template<class T>
void method_first( T& t, std::true_type has_first ) {
t.first();
}
template<class T>
void method_first( T& t, std::false_type has_first ) {
method_second( t, has_second<T&>{} );
}
template<class T>
void method( T& t ) {
method_first( t, has_first<T&>{} );
}
template<class T>
void method(T & t) {
if constexpr (has_first<T&>{}) {
t.first();
}
if constexpr (has_second<T&>{}) {
t.second();
}
}