2013-03-18 95 views
1

我有一个类Foo,有两个任意数据成员类型A和B.调用Foo :: operator()(Arg & &)将参数转发给两个成员并返回结果的总和。我可以看到几种方法来实施所有必要的类型扣除。有没有一些方法是首选,并减少编译器的压力?我的意思是编译时意义上的“紧张”,如果嵌套得太深,就会触及内部限制等等。你能概括一下它,还是对给定的编译器来说是非常具体的?返回类型推理:什么方法是首选?

我能做的“天真”自动decltype变种:

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创建者操作的辅助结构<>

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 <>并且是_blue <>。如果Arg为绿色,则Foo的运算符()应将Arg转发给A和B的成员函数为绿色,并返回结果的总和,类似于Arg为蓝色。一个类型永远不会是绿色和蓝色。应该可以添加更多的类型(所以使用bool值来表示不允许蓝色或绿色)。

一个变种会使用标签调度和经销商 - > decltype(...)尽可能:

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; 
}; 

另一个版本可以使用的辅助结构:

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; 
}; 

是任何版本的比较好?还有哪些其他方法可行?

+0

gcc-4.8用'-std = C++ 1y'和'auto'作为返回类型。 ;) – inf 2013-03-18 17:16:51

+0

我还没有读你写的所有。但是当你使用成员时,当你的成员函数是const的时候它也可以工作(成员将被视为const)。还需要在函数之前声明成员。就像你的代码一样,它不起作用。 – 2013-03-18 17:38:46

回答

0

我会避免所有的助手类/结构。每个帮助程序都要求编译器将其存储在某处并进行其他查找。如果没有这些类,编译器至少有机会优化它们,但我无法想象助手类能够改善所展示示例的情况。

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

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; 
}; 

我想也想象这是更容易维护,因人而异。对于编译时的表现,一如既往,只有一个真正的建议:测量它。

编辑:我改变的参数从Arg&&const Arg&和下降的双重std::forward<Arg>(...),因为这是违法的,看评论。

+1

'arg'被转发两次,一次为'm_a',一次为'm_b'。 – balki 2013-03-19 13:35:05

+0

@balki:很好的观察,但这是OP代码的问题。我会编辑我的答案,谢谢! – 2013-03-19 13:40:36

相关问题