C++将非模板成员函数调用转发到模板函数
C++ Forward non-template member function call to template function
我想在类"Record"中隐藏一个std::元组,并在其上提供一个运算符[]来访问元组的元素。不编译的天真代码是这样的:
#include <tuple>
template <typename... Fields>
class Record {
private:
std::tuple<Fields...> list;
public:
Record() {}
auto operator[](std::size_t n)
-> decltype(std::get<1u>(list)) {
return std::get<n>(list);
}
};
int main() {
Record<int, double> r;
r[0];
return 0;
}
g++4.6表示:
x.cc:13:32: error: no matching function for call to ‘get(std::tuple<int, double>&)’
x.cc:13:32: note: candidates are:
/usr/include/c++/4.6/utility:133:5: note: template<unsigned int _Int, class _Tp1, class _Tp2> typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/utility:138:5: note: template<unsigned int _Int, class _Tp1, class _Tp2> const typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(const std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/tuple:531:5: note: template<unsigned int __i, class ... _Elements> typename std::__add_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(std::tuple<_Elements ...>&)
/usr/include/c++/4.6/tuple:538:5: note: template<unsigned int __i, class ... _Elements> typename std::__add_c_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(const std::tuple<_Elements ...>&)
基本上,我想调用Record::operator[]
,就像调用数组一样。这可能吗?
get
的参数是编译时常数。您不能使用的运行时变量,并且不能有一个按照您的返回类型返回tuple
成员错误的你可以做的是滥用非类型参数推导:
#include <tuple>
template<typename... Args>
struct Foo {
std::tuple<Args...> t;
template<typename T, std::size_t i>
auto operator[](T (&)[i]) -> decltype(std::get<i>(t)) {
return std::get<i>(t);
}
// also a const version
};
int main()
{
Foo<int, double> f;
int b[1];
f[b];
return 0;
}
这太可怕了,我永远不会使用它,对用户来说也没有多大意义。我只想通过模板成员转发get
。
我将尝试解释为什么我认为这真的很糟糕:函数的返回类型只取决于编译时的事实(对于virtual
成员函数,这一点略有变化)。让我们假设在某些情况下,非类型参数推导是可能的(函数调用参数是constexpr
),或者我们可以构建一些相当好地隐藏它的东西,你的用户不会意识到他们的返回类型刚刚改变,隐式转换会对他们造成恶劣的影响。让这个明确的保险箱一些麻烦。
错误消息似乎具有误导性,因为代码的问题非常清楚:
auto operator[](std::size_t n)
-> decltype(std::get<1u>(list)) {
return std::get<n>(list);
}
模板参数n
到std::get
必须是常量表达式,但在您的代码中,n
以上是而不是常量表达式。
否。
不可能使用在运行时绑定的参数(如函数参数)作为模板参数,因为这需要在编译时绑定。
但让我们想象一下,它是:
Record<Apple, Orange> fruitBasket;
然后我们会有:
decltype(fruitBasket[0])
等于Apple
decltype(fruitBasket[1])
等于Orange
这里没有什么事困扰你吗?
在C++中,函数签名由其参数的类型(以及可选的模板参数值)定义。不考虑返回类型,也不参与过载解决(无论好坏)。
因此,您试图构建的函数根本没有意义。
现在,您有两种选择:
- 要求所有参数都继承或可转换为通用类型,并返回该类型(这允许您提出一个非模板函数)
- 接受模板,并要求用户专门提供他们希望使用的类型的索引
我不知道(也不能)在你的特定情况下,哪种选择更可取,这是你必须做出的设计选择。
最后,我要指出,你的推理水平可能太低了。您的用户真的需要独立访问每个字段吗?如果他们不这样做,你可以提供一些设施来依次将功能(访问者?)应用到每个元素。
我认为Xeo有这样的代码。
这是我的尝试,有些奏效。问题是[]
不是参考。
template<typename T, std::size_t N = std::tuple_size<T>::value - 1>
struct foo {
static inline auto bar(std::size_t n, const T& list)
-> decltype(((n != N) ? foo<T, N-1>::bar(n, list) : std::get<N>(list))) {
return ((n != N) ? foo<T, N-1>::bar(n, list) : std::get<N>(list));
}
};
template<typename T>
struct foo<T, 0> {
static inline auto bar(std::size_t n, const T& list)
-> decltype(std::get<0>(list)) {
return std::get<0>(list);
}
};
template <typename... Fields>
class Record {
private:
std::tuple<Fields...> list;
public:
Record() {
std::get<0>(list) = 5;
}
inline auto operator[](std::size_t n)
-> decltype(foo<decltype(list)>::bar(n, list)) {
return foo<decltype(list)>::bar(n, list);
}
};
int main() {
Record<int, double> r;
std::cout << r[0];
return 0;
}
由于n
是一个模板参数,它在编译时应该是已知的,但您希望在运行时将其作为参数传递。
此外,gcc 4.5.2也不高兴,因为这个事实:
g++ 1.cpp -std=c++0x
1.cpp: In member function 'decltype (get<1u>(((Record<Fields>*)0)->Record<Fields>::list)) Record<Fields>::operator[](size_t)':
1.cpp:14:25: error: 'n' cannot appear in a constant-expression
如果您对编译时间常数很满意,但仍然希望使用漂亮的operator[]
语法,这是一个有趣的解决方法:
#include <tuple>
template<unsigned I>
struct static_index{
static unsigned const value = I;
};
template <typename... Fields>
class Record {
private:
typedef std::tuple<Fields...> tuple_t;
tuple_t list;
public:
Record() {}
template<unsigned I>
auto operator[](static_index<I>)
-> typename std::tuple_element<
I, tuple_t>::type&
{
return std::get<I>(list);
}
};
namespace idx{
const static_index<0> _0 = {};
const static_index<1> _1 = {};
const static_index<2> _2 = {};
const static_index<3> _3 = {};
const static_index<4> _4 = {};
}
int main() {
Record<int, double> r;
r[idx::_0];
return 0;
}
Ideone上的实例。尽管我个人只是建议这样做:
// member template
template<unsigned I>
auto get()
-> typename std::tuple_element<
I, tuple_t>::type&
{
return std::get<I>(list);
}
// free function
template<unsigned I, class... Fields>
auto get(Record<Fields...>& r)
-> decltype(r.template get<I>())
{
return r.template get<I>();
}
Ideone上的实例。
- 将函数参数完美转发到函数指针:按值传递呢?
- 在按值调用 (c++) 中转发构造函数参数
- 使用函数指针转发声明作为 lamba 声明
- 完美的转发和构造函数
- 我可以在构造函数的主体中转发构造吗?
- 通过类型别名从构造函数转发模板推导
- 在可变参数函数中转发特定范围的参数
- 为什么添加析构函数(甚至是空的)会破坏我的结构,该结构使用 ref 转发和折叠来保存 ref 或值的副本?
- 如何在程序集函数中将元素数组作为参数传递时转发 ARM 寄存器的地址指针
- 如何通过可变参数模板将多个构造函数参数转发到数组初始值设定项列表?
- 类似函数的化简函数中的转发和返回类型
- 在编写包装现有函数并检查错误的模板函数时,如何使用完美转发?
- 在完美转发函数中公开参数类型,避免代码重复
- 从转发函数类型推断的模板化返回类型
- 完美转发函数以构建函数列表类
- 转发函数指针
- 如何在此转发函数中自动推导T
- 如何在 C++11 中使用可变参数模板正确创建显式参数转发函数
- 可以 QSignalMapper 转发函数参数
- 何时应该转发函数调用