4

我有模板类Reader

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

特殊实现(派生类)具有带有签名的方法

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给出的编译错误

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

我发现了这个错误,似乎没有修复。

有解决方法吗,我该怎么做?

4

3 回答 3

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放入第二个片段并尝试您的第二个或第三个解决方案,这次使用可变参数函数模板。也许它有效,谁知道呢。

于 2012-10-05T21:55:57.460 回答
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 });
}
于 2012-10-05T20:40:28.430 回答
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);
    }
};
于 2012-10-05T20:43:00.503 回答