创建类的可变包装器

时间:2012-10-05 20:33:01

标签: c++ templates lambda c++11 g++

我有模板类Reader

template<typename T>
class Reader{
    typedef T type;
};

特殊实现(derrived类)具有签名

的方法

T read(IStream&, any number of arguments, zero possible)

即班级IntegerReader公共职能:

template <typename T>
class IntegerReader : public Reader<T>{
public:
    T read(IStream& stream);
    T read(IStream& stream, T min, T max);
    T read(IStream& stream, T min, T max, std::string name);
}

现在我想创建一个包装器,这将允许我创建另一个读者,并将使用参数调用一些读者。

我试过这个:

template <typename T, typename... Args>
class ParametrizedReader : public Reader<typename T::type> {
    T reader;
    Args... args;
    ParametrizedReader(T reader, Args... args):reader(reader), args(args){
    }

    typename T::type read(IStream& stream){
        return reader.read(args..., stream);
    }
};
testlib/readerWrapper.hpp:7:6: error: expected unqualified-id before ‘...’ token
testlib/readerWrapper.hpp: In constructor ‘ParametrizedReader<T, Args>::ParametrizedReader(T, Args ...)’:
testlib/readerWrapper.hpp:8:61: error: class ‘ParametrizedReader<T, Args>’ does not have any field named ‘args’
testlib/readerWrapper.hpp: In member function ‘typename T::type ParametrizedReader<T, Args>::read(IStream&)’:
testlib/readerWrapper.hpp:12:22: error: ‘args’ was not declared in this scope

这样:

template <typename T, typename... Args>
class ParametrizedReader : public Reader<typename T::type> {
    std::function<T()> lambda;
    ParametrizedReader(T reader, Args... args){
        lambda = [=](IStream& stream){
            reader.read(stream, args...);
        };
    }

    typename T::type read(IStream& stream){
        return lambda(stream);
    }
};

testlib/readerWrapper.hpp:9:24: error: parameter packs not expanded with ‘...’:
testlib/readerWrapper.hpp:9:24: note:         ‘args’
testlib/readerWrapper.hpp:9:28: error: expansion pattern ‘args’ contains no argument packs

和此:

template <typename T, typename... Args>
class ParametrizedReader : public Reader<typename T::type> {
    std::function<T()> lambda;
    ParametrizedReader(T reader, Args... args){
        lambda = [reader, args...](IStream& stream){
            reader.read(stream, args...);
        };
    }

    typename T::type read(IStream& stream){
        return lambda(stream);
    }
};

testlib/readerWrapper.hpp:8:25: error: expected ‘,’ before ‘...’ token
testlib/readerWrapper.hpp:8:25: error: expected identifier before ‘...’ token
testlib/readerWrapper.hpp:8:28: error: parameter packs not expanded with ‘...’:
testlib/readerWrapper.hpp:8:28: note:         ‘args’

g ++ - 4.7

给出的编译错误

虽然我不确定第一个例子是否正确并且应该编译,但我相信第二个和第三个应该是。

我找到this bug,似乎没有修复。

是否有解决方法,我该怎样做我想要的?

3 个答案:

答案 0 :(得分:4)

您可以通过将参数绑定到lambda而不捕获它们来解决此问题。

ParametrizedReader(T reader, Args... args){
    lambda = std::bind(
        [=](IStream& stream, Args... as){
            reader.read(stream, as...);
        }, args...);
}

虽然您可能希望像@Alexandre所说的那样,而不是在确切的参数类型上参数化类模板:

template <typename T>
class ParametrizedReader : public Reader<typename T::type> {
    std::function<T()> lambda;
    template<typename... Args>
    ParametrizedReader(T reader, Args... args){
        lambda = std::bind(
            [=](IStream& stream, Args... as){
                reader.read(stream, as...);
            }, args...);
    }
    // ...
};

(注意:未经测试。)

可能还有效的方法是将std::bind放在第二个代码段中,尝试使用第二个或第三个解决方案,这次使用可变参数函数模板。也许它有效,谁知道。

答案 1 :(得分:3)

这让我想起了一个已知的错误:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=41933 它至少影响gcc 4.6.2和4.7.x系列。

Clang 3.1在处理lambda捕获中的可变参数时没有问题。

也许你可以走老式的路线并把东西存放到一个元组中:http://liveworkspace.org/code/7d4347021aaf004489591e78654f0233

#include <tuple>
#include <vector>
#include <string>

////////////////////////////////////
template<int ...>         struct seq                         {                         }; 
template<int N, int ...S> struct gens : gens<N-1, N-1, S...> {                         }; 
template<int ...S>        struct gens<0, S...>               { typedef seq<S...> type; }; 

////////////////////////////////////

template<typename T>
struct Reader
{
    typedef T type;
};

//Special implementations (derrived classes) have methods with signature
struct IStream {};

template <typename T>
class IntegerReader : public Reader<T>
{
public:
    T read(IStream& stream);
    T read(IStream& stream, T min, T max);
    T read(IStream& stream, T min, T max, std::string name);
};

template <typename T, typename... Args>
class ParametrizedReader : public Reader<typename T::type>
{
    T _reader;
    std::tuple<Args...> _args;
public:
    ParametrizedReader(T reader, Args... args)
        : _reader(reader), _args(std::forward<Args>(args)...)
    {
    }

    typename T::type read(IStream& stream)
    {
        callFunc(typename gens<sizeof...(Args)>::type());
    }

    template<int ...S>
        void callFunc(seq<S...>)
        {
            func(std::get<S>(_args) ...);
        }
};

template <typename T, typename... Args>
ParametrizedReader<T, Args...> make_parameterized_reader(T reader, Args... args)
{
    return ParametrizedReader<T, Args...>(reader, std::forward<Args>(args)...);
}

int main(int argc, const char *argv[])
{
    Reader<char> reader;
    auto pr = make_parameterized_reader(reader, "stuff", 3.14, std::string("you can think of"), std::vector<int> { 1,2,3 });
}

答案 2 :(得分:0)

我从类模板中删除参数包,使用完美转发和std :: bind。

template <typename T>
class ParametrizedReader : public Reader<typename T::type> 
{
    std::function<T()> lambda;

    template <typename... Args>
    ParametrizedReader(T reader, Args&&... args)
    {
        using namespace std::placeholders;

        lambda = std::bind(
            std::mem_fn(&Reader::read),
            _1,
            std::forward<Args>(args)...);
    }

    typename T::type read(IStream& stream)
    {
        return lambda(stream);
    }
};
相关问题