返回类型推断:首选哪种方法
Return type deduction: What method is preferred?
我有一个类Foo,它有两个类型为a和b的任意数据成员。调用Foo::operator()(Arg &&)将参数转发给这两个成员并返回结果的和。我可以看到几种方法来实现所有必要的类型扣除。是否有一些方法是首选的,并且对编译器的压力较小?我指的是编译时意义上的"紧张",如果嵌套得太深,就会达到内部限制,等等。你能概括一下吗,还是说它是特定于某个编译器的?
我可以使用"naive"自动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<>和is_blue <>。如果Arg为绿色,Foo的运算符()将把Arg转发给A和B的成员函数绿色,并返回结果的总和,类似于Arg为蓝色。一个字体不可能同时是绿色和蓝色。应该可以添加其他类型(因此不允许使用bool值来表示蓝色或绿色)。
一种变体将尽可能使用标记调度和auto -> 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;
};
有更好的版本吗?还有其他可能的方法吗?
我会避免所有的辅助类/结构。每个帮助器都要求编译器将其存储在某个地方,并进行额外的查找。如果没有这些类,编译器至少有机会进行优化,但是我无法想象一个helper类可以改善您所展示的示例中的情况。
对于您的绿色/蓝色示例,我甚至会考虑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;
};
我也想象这更容易维护,YMMV。对于编译时性能,一如既往,只有一个真正的建议:度量它。
EDIT:我将参数从Arg&&
更改为const Arg&
,并删除了双std::forward<Arg>(...)
,因为这是非法的,参见注释
- 我的模板类方法返回错误类型?
- QtQuick - qml:28:错误:未知方法返回类型:自定义类型
- C++方法是否可以根据传递给构造函数的参数具有不同的返回类型?
- 具有引用返回类型的重写方法上的协变返回类型无效
- Arduino trim() 和 replace() 方法返回从 'void' 到非标量类型'String'请求的转换
- Java 调用 dll 字符串返回类型方法
- 必须使用尾随返回类型的示例,因为无法用旧方法解决问题
- 如何拥有两个名称相同但返回类型不同的纯虚拟方法
- 可能从单个方法返回不同类型的对象
- qml 未知方法返回类型:ArchiveFile*,即使调用了 qmlRegisterUncreatableType
- std::d eclval vs crtp,无法从不完整类型推断方法返回类型
- 将返回类型推断为模板参数类型方法
- dlsym() 解决方法返回类型
- 我如何根据某些模板参数影响模板类方法的返回类型
- 如何检查模板类方法返回类型
- 覆盖方法返回类型,在C 中使用不完整的派生类
- 类成员方法 - 返回类型模板
- C++中具有多态模板类的未知方法返回类型
- 是否可以使用Boost概念检查库验证方法返回类型
- 使用不同方法返回类型的C++模板类设计