C ++模板:返回新类型的正确方法

时间:2018-02-05 11:24:02

标签: c++ c++11 templates variadic-templates

对于通用标题感到抱歉,但我无法集中解决问题。

我有一个模板化的类方法,它接受一个参数包并提供一个新的类型作为回报,以隐藏实现的细节。更具体地说,该类处理SQLite查询,并且方法调用sqlite3_prepare()以在执行查询之前准备语句。

class Table {
   ...
   template <typename ...Ts>
   class PreparedStatement { ... };

   template <typename ...Ts>
   PreparedStatement<Ts...> prepare(std::tuple<Ts...> tuple) {
      // do something
      return PreparedStatement<Ts...> ( ... );
   }

适用于“普通”类型,但在声明参数const时会出现问题:

const Field<int> fld = createField<int>("name");
...
PreparedStatement<decltype(fld)> s = prepare(make_tuple(fld));

错误如下:

no match for 'operator =' (operand types are PreparedStatenent<const Field<int>> and PreparedStatement<Field<int>>

我怀疑问题出在我的函数声明中,有没有办法解决这个问题并使函数更“优雅”?

注意:我知道我可以通过手动声明s变量来解决问题,但我怀疑这个方法是如何实现的。

正如许多人所问,这是一个例子:

#include <tuple>

template <typename T>
struct Field {
};

class Table {
public:
   template <typename ...Ts>
   class PreparedStatement {
       public:
       PreparedStatement() {};
    };

   template <typename ...Ts>
   PreparedStatement<Ts...> prepare(std::tuple<Ts...> tuple) {
      // do something
      return PreparedStatement<Ts...> ( );
   }
};


int main() 
{
    Field<int> fld;  
    Table t;

    Table::PreparedStatement<decltype(fld)> p;
    p = t.prepare(std::make_tuple(fld));

    // here comes the problem
    const Field<int> f2;
    Table::PreparedStatement<decltype(f2)> p2;

    p2 = t.prepare(std::make_tuple(f2));    

    return 0;
}

这是编译器输出

  

main.cpp:在函数'int main()'中:main.cpp:35:39:错误:不匹配   'operator ='(操作数类型是'Table :: PreparedStatement&gt;'和'Table :: PreparedStatement&gt;')        p2 = t.prepare(std :: make_tuple(f2));                                          ^ main.cpp:10:10:注意:候选人:constexpr表:: PreparedStatement&gt;&amp;   Table :: PreparedStatement&gt; :: operator =(const   Table :: PreparedStatement&gt;&amp;)       class PreparedStatement {             ^ ~~~~~~~~~~~~~~~~ main.cpp:10:10:注意:“Table :: PreparedStatement&gt;”中没有已知的参数1转换   'const Table :: PreparedStatement&gt;&amp;'   main.cpp:10:10:注意:候选人:constexpr   Table :: PreparedStatement&gt;&amp;   表:: PreparedStatement的

     
    

:: operator =(Table :: PreparedStatement&gt;&amp;&amp;)main.cpp:10:10:注意:参数1没有已知的转换     'Table :: PreparedStatement&gt;'至     'Table :: PreparedStatement&gt;&amp;&amp;'

  

更新 正如许多人所指出的,我可以使用auto推断出类型,但在某些情况下auto实际上无法使用。例如,一个是我需要在类上下文中声明语句。 因此,假设auto由于某种原因被禁止。没有任何其他解决方案可用吗?请参阅上面的更新代码。

2 个答案:

答案 0 :(得分:3)

cppreference.com make_tuple告诉我们:

  

template< class... Types > tuple<VTypes...> make_tuple( Types&&... args );

     

对于Ti中的每个Types...Vi中的相应类型Vtypes...为   std::decay<Ti>::type除非std::decay的应用导致   对于某种类型std::reference_wrapper<X>X,在这种情况下推导出来   类型为X&

虽然std::decay除了其他内容,但删除了cv限定符。因此,您的类型不是PreparedStatement<const Field<int>>,而是PreparedStatement<Field<int>>

您可以像manni66提议的那样使用auto来避免此类问题。

auto s = prepare(make_tuple(fld));

答案 1 :(得分:1)

  

我可以使用auto来推断类型,但在某些情况下auto实际上不能使用。例如,一个是我需要在类上下文中声明语句。所以假设出于某种原因禁止使用auto。没有任何其他解决方案可用吗?请参阅上面的更新代码。

您可以使用auto表达式来计算decltype返回的值,而不是prepare

我的意思是......而不是

Table::PreparedStatement<decltype(f2)> p2;

你可以试试

decltype(t.prepare(std::make_tuple(f2))) p2;

decltype(std::declval<Table>().prepare(
   std::make_tuple(std::declval<Field<int>>()))) p2;

我想您也可以使用类似的decltype()来声明您的班级成员。