C++ C++;将非模板成员函数调用转发到模板函数

C++ C++;将非模板成员函数调用转发到模板函数,c++,function,templates,c++11,tuples,C++,Function,Templates,C++11,Tuples,我想在我的类“Record”中隐藏一个std::tuple,并在其上提供一个操作符[]来访问该tuple的元素。未编译的原始代码如下所示: #include <tuple> template <typename... Fields> class Record { private: std::tuple<Fields...> list; public: Record() {} auto operator[](std::siz

我想在我的类“Record”中隐藏一个std::tuple,并在其上提供一个操作符[]来访问该tuple的元素。未编译的原始代码如下所示:

#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;
}
#包括
模板
课堂记录{
私人:
std::元组列表;
公众:
记录(){}
自动运算符[](标准::大小\u t n)
->decltype(std::get(list)){
返回std::get(列表);
}
};
int main(){
记录r;
r[0];
返回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 ...>&)
x.cc:13:32:错误:调用“get(std::tuple&)”时没有匹配的函数
x、 抄送:13:32:注:候选人为:
/usr/include/c++/4.6/实用程序:133:5:注意:模板typename std::tuple_元素::type&std::get(std::pair&)
/usr/include/c++/4.6/实用程序:138:5:注意:模板consttypename std::tuple\u元素::type&std::get(conststd::pair&)
/usr/include/c++/4.6/tuple:531:5:注意:模板typename std::\uuu add\u ref::type std::get(std::tuple&)
/usr/include/c++/4.6/tuple:538:5:注意:模板类型名称std::\uuu add\u c\u ref::type std::get(const std::tuple&)

基本上,我想调用
Record::operator[]
,就像在数组上一样。这可能吗?

因为
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
g++1.cpp-std=c++0x
1.cpp:在成员函数“decltype(get((Record*)0)->Record::list”)Record::operator[](size_t)中:
1.cpp:14:25:错误:“n”不能出现在常量表达式中

get的参数是编译时常量。您不能使用 运行时变量,您不能有一个函数 返回返回类型为的
元组成员
错。您可以做的是滥用非类型参数推断:

#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;
}
#包括
模板
结构Foo{
std::元组t;
模板
自动运算符[](T(&)[i])->decltype(std::get(T)){
返回std::get(t);
}
//也是常量版本
};
int main()
{
福福;
int b[1];
f[b];
返回0;
}
这太可怕了,我永远不会使用它,对用户来说也没有多大意义。我只需通过模板成员转发
get


我将试着解释为什么我认为这真的很糟糕:函数的返回类型只取决于编译时事实(对于
virtual
成员函数,这一点略有变化)。让我们假设在某些情况下(函数调用参数是
constexpr
),非类型参数推断是可能的,或者我们可以构建一些相当好地隐藏它的东西,您的用户不会意识到他们的返回类型刚刚更改,隐式转换会对他们造成不好的影响。明确这一点可以避免一些麻烦。

错误消息似乎具有误导性,因为代码的问题非常明显:

 auto operator[](std::size_t n)
            -> decltype(std::get<1u>(list)) {
        return std::get<n>(list);
    }
auto操作符[](标准::大小\u t n)
->decltype(std::get(list)){
返回std::get(列表);
}

模板参数
n
std::get
必须是一个常量表达式,但在上面的代码中
n
不是常量表达式

我认为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;
}
模板
结构foo{
静态内联自动栏(标准::大小、常数和列表)
->decltype(((n!=n)?foo::bar(n,list):std::get(list))){
return((n!=n)?foo::bar(n,list):std::get(list));
}
};
模板
结构foo{
静态内联自动栏(标准::大小、常数和列表)
->decltype(std::get(list)){
返回std::get(列表);
}
};
模板
课堂记录{
私人:
std::元组列表;
公众:
记录(){
标准::获取(列表)=5;
}
内联自动运算符[](标准::大小\u t n)
->decltype(foo::bar(n,list)){
返回foo::bar(n,list);
}
};
int main(){
记录r;
标准::cout

不可能使用在运行时绑定的参数(例如函数参数)作为模板参数,因为这样的参数需要在编译时绑定

但让我们想象一下,它是:

Record<Apple, Orange> fruitBasket;
记录果篮;
那么我们就可以:

  • decltype(果篮[0])
    equals
    Apple
  • decltype(果篮[1])
    equals
    Orange
这里有什么事困扰着你吗

C++中,函数签名是由其参数的类型定义的(可选的是模板参数的值)。在过载解决方案中,不考虑返回类型,并且不参与(最好或更坏)。 因此,您试图构建的函数根本没有意义

现在,您有两种选择:

  • 要求所有参数继承或转换为公共类型,并返回该类型(允许您提出非模板函数)
  • 采用模板,并要求您的用户专门提供他们希望使用的类型的索引
我不知道(也不能)在您的特定情况下,哪种方案更可取,这是您必须做出的设计选择


最后,我要指出的是,您的推理水平可能太低。您的用户真的需要独立访问每个字段吗?如果他们不需要,您可以提供工具,将函数(访问者?)依次应用于每个元素。例如,如果您对编译时常数没有问题,但仍然希望使用nice
操作符[]
语法,这是一个有趣的解决方法:

#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;
}
#包括
模板
结构静态索引{
静态无符号常量值=I;
};
模板
课堂记录{
私人:
typedef std::tuple tuple\t;
元组列表;
公众:
记录(){}
模板
金
// 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>();
}