jeudi 17 mai 2018

Why does this template code fail to compile in C++11 even without instantiating anything?

Working on a parser combinator library, and I want to limit the types of things my combinators will accept. They have to be a "parser" which basically means they're convertible to a parser class that's a thin wrapper generally. I am, however, having trouble stating that through the type system. This is a dissected example:

#include <string>
#include <functional>
#include <type_traits>

using namespace std;

// parser can be built from a certain callable
template <typename P,
          typename enable_if<
              is_convertible<P, function<bool(string)>>::value
            >::type* = nullptr
          >
struct parser {
    parser(P p)
        : p_(p) {}

    // just call contained parser
    bool operator()(string stream) {
        return p_(stream);
    }

private:
    P p_;
};


// type function to validate that a type is a parser
template <typename A, typename B=void>
using is_parser =
    std::enable_if<
      std::is_convertible<A, parser<A>>::value && 
     (std::is_convertible<B, parser<B>>::value || std::is_void<B>::value)
    >;


// invert result of parser
template <typename A, typename is_parser<A>::type* = nullptr>
struct notparser {
    notparser(A p)
        : p_(p) {}

    bool operator()(string stream) {
        return !p_(stream);
    }

private:
    parser<A> p_;
};


// not operator
template <typename A, typename is_parser<A>::type* = nullptr>
notparser<A> operator !(A a) {
    return notparser<A>(a);
}

Even without instantiating anything, or even a main function, this generates an error in g++ 5.4.0:

test.cc: In substitution of ‘template<class A, class B> using is_parser = std::enable_if<(std::is_convertible<A, parser<A> >::value && (std::is_convertible<B, parser<B> >::value || std::is_void<_Arg>::value))> [with A = A; B = void]’:
test.cc:38:43:   required from here
test.cc:33:48: error: no type named ‘type’ in ‘struct std::enable_if<false, void>’
      (std::is_convertible<B, parser<B>>::value || std::is_void<B>::value)
                                                ^
test.cc:33:48: note: invalid template non-type parameter

The only weirdness I see is the substitution into is_parser with A=A, is this a template-expansion-phase thing?

Aucun commentaire:

Enregistrer un commentaire