首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >返回类型推演:首选哪种方式?

返回类型推演:首选哪种方式?
EN

Stack Overflow用户
提问于 2013-03-19 00:06:47
回答 1查看 168关注 0票数 1

我有一个类Foo,它有两个类型为A和B的任意数据成员。调用Foo::operator()(Arg &&)将参数转发给这两个成员,并返回结果的总和。我可以看到几种方法来实现所有必要的类型演绎。有没有一些方法是首选的,并且对编译器施加的压力更小?我的意思是在编译时间意义上的“紧张”,如果嵌套太深就会达到内部限制,等等。你能概括一下吗,或者它是高度特定于给定的编译器吗?

我可以使用"naive“auto-decltype变体:

代码语言:javascript
复制
template <typename A, typename B>
class Foo
{
public:
  Foo(A a, B b) : m_a(std::move(a)), m_b(std::move(b)) { }

  template <typename Arg>
  auto operator()(Arg && arg) -> decltype(m_a(std::forward<Arg>(arg)) + m_b(std::forward<Arg>(arg)))
  {
    return m_a(std::forward<Arg>(arg)) + m_b(std::forward<Arg>(arg));
  }
private:
  A m_a;
  B m_b;
};

我可以编写一个帮助器结构,它只在类型上操作,而不是在“真实”实例上操作,而是在std::declval<>创建的实例上操作

代码语言:javascript
复制
template <typename A, typename B, typename Arg>
struct Foo_Returns
{
  typedef decltype(std::declval<A>()(std::declval<Arg>()) +
                   std::declval<B>()(std::declval<Arg>())) type;
}

template <typename A, typename B>
class Foo
{
public:
  Foo(A a, B b) : m_a(std::move(a)), m_b(std::move(b)) { }

  template <typename Arg>
  typename Foo_Returns<A, B, Arg>::type
  operator()(Arg && arg)
  {
    return m_a(std::forward<Arg>(arg)) + m_b(std::forward<Arg>(arg));
  }
private:
  A m_a;
  B m_b;
};

还有更多的可能性吗?

现在让它变得更难:我们有两个特征is_green<>和is _blue<>。如果Arg是绿色的,Foo的运算符()将把Arg传递给A的成员函数绿色和B的成员函数绿色,并返回结果的总和,如果Arg是蓝色的话也是类似的。一个类型永远不会同时是绿色和蓝色。应该可以添加其他类型的风格(因此不允许使用布尔值来表示蓝色或绿色)。

一种变体将使用标记分派和自动->解密类型(...)只要有可能:

代码语言:javascript
复制
struct green_tag { };
struct blue_tag { };
struct error_tag;

template <typename T>
struct category
{
  typedef typename std::conditional<is_green<T>::value, 
                                    green_tag,
                                    typename std::conditional<is_blue<T>::value,
                                                              blue_tag,
                                                              error_tag
                                                             >::type
                                   >::type type;
}

template <typename A, typename B>
class Foo
{
public:
  Foo(A a, B b) : m_a(std::move(a)), m_b(std::move(b)) { }

  template <typename Arg>
  auto operator()(Arg && arg) -> decltype(impl(std::forward<Arg>(arg), typename category<Arg>::type()))
  {
    return impl(std::forward<Arg>(arg), typename category<Arg>::type());
  }

private:
  template <typename Arg>
  auto impl(Arg && arg, green_tag) -> decltype(m_a.green(std::forward<Arg>(arg)) + m_b.green(std::forward<Arg>(arg)))
  {
    return m_a.green(std::forward<Arg>(arg)) + m_b.green(std::forward<Arg>(arg));
  }

  template <typename Arg>
  auto impl(Arg && arg, blue_tag) -> decltype(m_a.blue(std::forward<Arg>(arg)) + m_b.blue(std::forward<Arg>(arg)))
  {
    return m_a.blue(std::forward<Arg>(arg)) + m_b.blue(std::forward<Arg>(arg));
  }

  A m_a;
  B m_b;
};

另一个版本可以使用helper structs:

代码语言:javascript
复制
template <typename A, typename B, typename Arg, typename Category = typename category<Arg>::type>
struct Foo_Returns;

template <typename A, typename B, typename Arg>
struct Foo_Returns<A, B, Arg, green_tag>
{
  typedef decltype(std::declval<A>().green(std::declval<Arg>()) +
                   std::declval<B>().green(std::declval<Arg>())) type;

  type operator()(A & a, B & b, Arg && arg) const
  {
    return a.green(std::forward<Arg>(arg)) + b.green(std::forward<Arg>(arg));
  }  
};

template <typename A, typename B, typename Arg>
struct Foo_Returns<A, B, Arg, blue_tag>
{
  typedef decltype(std::declval<A>().blue(std::declval<Arg>()) +
                   std::declval<B>().blue(std::declval<Arg>())) type;

  type operator()(A & a, B & b, Arg && arg) const
  {
    return a.blue(std::forward<Arg>(arg)) + b.blue(std::forward<Arg>(arg));
  }  
};

template <typename A, typename B>
class Foo
{
public:
  Foo(A a, B b) : m_a(std::move(a)), m_b(std::move(b)) { }

  template <typename Arg>
  typename Foo_Returns<A, B, Arg>::type
  operator()(Arg && arg)
  {
    return Foo_Returns<A, B, Arg>()(m_a, m_b, std::forward<Arg>(arg));
  }

private:
  A m_a;
  B m_b;
};

有没有更好的版本?还可以使用哪些其他方法?

EN

回答 1

Stack Overflow用户

发布于 2013-03-19 01:13:28

我会避免所有的助手类/结构。每个helper都需要编译器将其存储在某个位置并进行额外的查找。如果没有这些类,编译器至少有机会进行优化,但我无法想象helper类可以改善您所展示的示例中的情况。

对于你的绿色/蓝色示例,我甚至会考虑SFINAE,以保持代码更短,实例化的类/方法的数量更少:

代码语言:javascript
复制
template <typename A, typename B>
class Foo
{
public:
  Foo(A a, B b) : m_a(std::move(a)), m_b(std::move(b)) { }

  template <typename Arg>
  auto operator()(const Arg & arg) ->
    typename std::enable_if< is_green<Arg>::value,
      decltype(m_a.green(arg) + m_b.green(arg) >::type
  {
    return m_a.green(arg) + m_b.green(arg);
  }

  template <typename Arg>
  auto operator()(const Arg & arg) ->
    typename std::enable_if< is_blue<Arg>::value,
      decltype(m_a.blue(arg) + m_b.blue(arg)) >::type
  {
    return m_a.blue(arg) + m_b.blue(arg);
  }

private:
  A m_a;
  B m_b;
};

我也认为这会更容易维护,YMMV。对于编译时性能,一如既往,只有一个真正的建议:测量它。

编辑:我将参数从Arg&&更改为const Arg&,并删除了双std::forward<Arg>(...),因为这是非法的,请参阅注释。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15481425

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档