C++ traits classes to detect member function: compiler error in MSVC 2015 RC

580 views Asked by At

The following traits classes are used to check whether a generic vector has operator[](int) or operator()(int):

template<typename ...> using void_t = void;

template< typename, typename = void>
struct has_bracket_operator : public std::false_type {};

template<typename V>
struct has_bracket_operator<V, void_t<decltype(std::declval<V>()[int{}])> >
    : public std::true_type{};

template<typename, typename = void>
struct has_parenthesis_operator : public std::false_type {};

template<typename V>
struct has_parenthesis_operator<V, void_t<decltype(std::declval<V>()(int{}))> >
    : public std::true_type{};

It seems to compile well in gcc 5.1.0


On the other hand, in the latest MSVC download (Microsoft Visual Studio Community 2015 RC Version 14.0.22823.1 D14REL), I get a compiler error for the has_parenthesis_operator template which reads:

error C2064: term does not evaluate to a function taking 1 arguments

Moreover, the following version I find preferable do both not compile at all (note that operator[](int{}) is used instead of [int{}], and similar for the parenthesis):

template<typename V>
struct has_bracket_operator<V, void_t<decltype(std::declval<V>().operator[](int{}))> >
    : public std::true_type{};

//...

template<typename V>
struct has_parenthesis_operator<V, void_t<decltype(std::declval<V>().operator()(int{}))> >
    : public std::true_type{};

The error message here is

error C2228: left of '.[]' must have class/struct/union
note: type is 'add_rvalue_reference<V>::type'
error C2228: left of '.()' must have class/struct/union
note: type is 'add_rvalue_reference<V>::type'

Questions:

  • Is this a bug? Or am I doing something wrong (which gcc accepts somehow)?
  • Is there a workaround by which I can use the above techniques (particularly void_t)
  • If a workaround is not possible, how to set up an alternate SFINAE check in MSVC (I tried much of this but didn't really succeed -- at least in in MSVC)?
1

There are 1 answers

0
davidhigh On

As mentioned by @T.C., the above approach via void_t<decltype(...)> requires expression SFINAE, which is not available in the present version of MSVC 2015.

Here is the current status on the implementation of expression SFINAE, taken from here:

Q. When will you implement Expression SFINAE?

A. We're planning to start implementing Expression SFINAE in the compiler immediately after 2015 RTM, and we're planning to deliver it in an Update to 2015, supported for production use. (But not necessarily 2015 Update 1. It might take longer.)

To be extra clear, this applies to the compiler only, not the libraries. The C++11 Standard Library mandated the use of Expression SFINAE in a small number of places, which we've actually already implemented. (The places are is_assignable and allocator_traits, which the compiler has taken special care to support.) The C++14 Standard Library added two more places where Expression SFINAE must be used: std::function construction/assignment and result_of. I'll modify the STL when the compiler is ready, but I'm not planning to ship those library changes in an Update. ...



Moreover, here is an a bit more old-fashioned workaround for the above traits using traditional SFINAE (inspired from here):

template<typename C, typename Ret, typename... Args>
struct has_parenthesis_operator<C, Ret(Args...)>
{
private:
    template<typename T>
    static constexpr auto check(T*) 
        -> typename std::is_same<std::decay_t<decltype(std::declval<T>().operator()(std::declval<Args>()...))>, Ret>::type;

    template<typename> static constexpr std::false_type check(...);

    typedef decltype(check<C>(0)) type;

public:
    static constexpr bool value = type::value;
};

which can be used for example as

template<typename V
       , typename = std::enable_if_t<has_bracket_operator<V, double(int)>::value>
auto get(V const& v, int i)
{
    return v[i];
}

This checks whether the passed vector type has a member double operator[](int), and if so calls it (--otherwise one can do similar with the parenthesis operator).



Regardign the comment by @Yakk: I encountered the __if_exists statement special to MSVC which seems to fulfill a similar task than the above.