使用rvalue引用重载模板化的可变参数运算符

时间:2018-04-13 10:23:55

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

这是我的定义:

template<typename ... TL>
struct TemplatedType { };

template<typename ... TL>
std::istream& operator>>(std::istream& is, TemplatedType<TL ...> & sp)
{
    // do stuff

    return is;
}

用法:

std::istringstream iss("I'd like some pancakes, please");
TemplatedType<int> a;
iss >> a;

它的效果很好,但是我希望将模板化的参数作为r值引用

template<typename ... TL>
std::istream& operator>>(std::istream& is, const TemplatedType<TL && ...> & sp) {...}

然后,编译器开始大喊:

  

C2678二进制'&gt;&gt;':找不到运算符,它采用左手操作数   'std :: istringstream'类型(或没有可接受的转换)

问题出在哪里?

动机: 我想创建一个可以这种方式使用的split函数:

std::istringstream iss("alpha:=10/50.1");

std::string x;
int y;
double z;

iss >> split(x, ':', '=', y, '/', z); // sets x=alpha, y=10, z=50.1

因此该函数需要能够接收l值和r值引用。

2 个答案:

答案 0 :(得分:2)

有趣的问题。

Jarod42解释了问题所在:如果定义TemplatedType<int> a;,则可以匹配

template<typename ... TL>
std::istream& operator>>(std::istream& is,
                         const TemplatedType<TL ...> & sp)

但无法匹配

template<typename ... TL>
std::istream& operator>>(std::istream& is, 
                         const TemplatedType<TL & ...> & sp)

因为您有int并且需要int&

但是如何开始工作

iss >> split(x, ':', '=', y, '/', z); 

嗯......我想你可以在std::tuple

中加TemplatedType
template <typename ... TL>
struct TemplatedType
 { std::tuple<TL...> t; };

所以split()(我已将其重命名为mySplit())只是变成

template <typename ... TL>
TemplatedType<TL...> mySplit (TL && ... al)
 { return { std::forward_as_tuple(al...) }; }

mySplit(x, ':', '=', y, '/', z);

其中xstd::stringyintzdouble,您获得TemplatedType<std::string &, char, char, int &, char, double &> }。

您可以编写一个调用第一个辅助函数的operator<<()

template <typename ... TL>
std::istream & operator>> (std::istream & is,
                           TemplatedType<TL...> const & sp)
 {
   myHelper1(is, sp, std::index_sequence_for<TL...>{});

   return is;
 }

sp中包含的元组中的第一个辅助函数提取元素,并调用第二个辅助函数

template <typename ... TL, std::size_t ... IL>
void myHelper1 (std::istream & is,
                TemplatedType<TL...> const & sp,
                std::index_sequence<IL...> const &)
 { myHelper2(is, std::get<IL>(sp.t)...); }

第二个辅助函数稍微复杂一些。

它是一个递归的函数集,每次调用都会消耗一个/两个元素。

首先,地面(终端)案例

void myHelper2 (std::istream &)
 { }

然后是具有以下常量std::string的特殊char情况,分隔符(没有分隔符,如果您只是编写is >> s,则会获得is中包含的完整字符串})

template <typename ... TS>
void myHelper2 (std::istream & is, std::string & s, char const & delim,
                TS && ... ts)
 {
   std::getline(is, s, delim);

   myHelper2(is, std::forward<TS>(ts)...);
 }

接下来在您的示例中接收常量字符(例如:=/的版本;但第一个字符串作为字符串的分隔符使用)并丢弃来自char的{​​{1}}(如果需要,请检查丢弃的char是否与char相同)

is

最后一般情况,接收通用(类型template <typename ... TS> void myHelper2 (std::istream & is, char const ch, TS && ... ts) { char ch2; is >> ch2; // check if `ch` == `ch2`? exception otherwise? myHelper2(is, std::forward<TS>(ts)...); } )引用

T

以下是一个完整的例子。

从C ++ 14开始工作,因为{+ 1}}和template <typename T, typename ... TS> void myHelper2 (std::istream & is, T & t, TS && ... ts) { is >> t; myHelper2(is, std::forward<TS>(ts)...); } 是在C ++ 14中引入的。但是如果你想要一个C ++ 11解决方案,为他们写一个sobstitute很简单

std::index_sequence

答案 1 :(得分:0)

理论上如果TemplatedType的实现方式与std:tuple相同(或者这是一个元组),并且所有成员类型都存在>>个运算符。

例如:

#include <istream>
#include <iostream>
#include <tuple>

namespace detail {

template<std::size_t... Is>
struct seq { };

template<std::size_t N, std::size_t... Is>
struct gen_seq : gen_seq<N - 1, N - 1, Is...> { };

template<std::size_t... Is>
struct gen_seq<0, Is...> : seq<Is...> { };

template<typename T, typename F, std::size_t... Is>
void for_each(T&& t, F&& f, seq<Is...>)
{
    auto l = { (f(std::get<Is>(t)), 0)... };
}

template<typename... Ts, typename F>
void tuple_for_each(std::tuple<Ts...>& t, F&& f)
{
    for_each(t, std::forward<F>(f), detail::gen_seq<sizeof...(Ts)>());
}

class deserializer {
    deserializer(const deserializer&) = delete;
    deserializer& operator=(const deserializer&) = delete;
public:
    constexpr deserializer(std::basic_istream<char>& is) noexcept:
        is_(is)
    {}
    template<typename any_type>
    void operator()(any_type& val) {
        is_ >> val;
    }
private:
    std::basic_istream<char>& is_;
};

} // namespace detail

template<typename...__tl>
std::basic_istream<char>& operator >> (std::basic_istream<char>& s, std::tuple<__tl...> &out)
{
    detail::tuple_for_each( out, detail::deserializer(s) );
    return s;
}


int main(int argc, const char** argv)
{
    std::cout<< "Please input a digit" << std::endl;
    std::tuple<int> int_only_tuple;
    std::cin >> int_only_tuple;
    std::cout<< "A digit: " << std::get<0>(int_only_tuple)  << std::endl;

    std::cout<< "Please input two strings" << std::endl;

    std::tuple<std::string, std::string> in;

    std::cin >> in;

    std::cout << "First string: " << std::get<0>(in) << std::endl;
    std::cout<< "Second string: " << std::get<1>(in) << std::endl;

    return 0;
}

另见: