Question

The original problem I tried to solve when stumbled upon this was to select parse_impl version:

  • if the parser (of type U) provides a field named "skp", use that field;
  • if not, use a default value.

I came up with the following code:

// This variant compiles for parsers requiring a skipper:
template <typename I, typename U, typename A,
          typename = typename std::enable_if<
              not std::is_same<
                  typename std::remove_reference<U>::type::skipper_type,
                  qi::unused_type
              >::value
          >::type,
          typename = void > // avoid redefinition (1 more overload not shown)
bool parse_impl(I & start, I end, U && parser, A & attr)
{
    // qi::space by default:
    return qi::phrase_parse(start, end, parser, qi::space, attr);
}

// This variant compiles for parsers providing skipper via 'skp' member:
template <typename I, typename U, typename A,
          typename = typename std::enable_if<
              not std::is_same<
                  typename std::remove_reference<U>::type::skipper_type,
                  qi::unused_type
              >::value
              && (sizeof(U::skp) != 0)
          >::type,
          typename = void, typename = void > // avoid redefinition
bool parse_impl(I & start, I end, U && parser, A & attr)
{
    // parser.skp is available:
    return qi::phrase_parse(start, end, parser, parser.skp, attr);
}

The call site look like this:

pr.is_ok = parse_impl(pr.position, input.cend(), parser, pr.attr);

and this is called both for types having skp and ones that haven't.

And it compiles (on gcc4.7), but I don't understand why: when skp is present, expressions in both enable_ifs should evaluate to true (skipper_type is obviously not equal to unused_type then), and the call should be ambiguous. Where am I mistaken?

Was it helpful?

Solution

The problem here is, as concluded in the comments, that when using just U::skp, it might be that U is deduced as a reference type (i.e., when passing an lvalue parser). When this happens, you get SFINAE going, as a reference type obviously has no nested anything.

The fix is to remove the reference from U with std::remove_reference so that you have (sizeof(std::remove_reference<T>::type::skp) != 0). Note that typename is not needed here, since ::skp indicates that type has to be a typename.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top