基于方法专门化模板 最近我在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(); } }

基于方法专门化模板 最近我在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

所以我的问题是:通过专门化每一个案例,在不创建不必要的boileplate代码的情况下实现这样的行为是可能的吗?

您将使用我们称之为sfinae的东西,而不是
。这是一种基于参数类型在函数上添加常量的技术

下面是您在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();
  }
}