Search code examples
c++templatesvisual-c++

C++ templated parameter pack compiles on gcc/clang, error on MSVC


I have boiled my error down to this case. I have a snippet of a custom variant based on the variant code in maplibre-gl. This inherits from std::variant with some extra template checks on its own constructor. I define my own BaseVariant as an instance of this variant, and then define two classes: ValueFromBaseVariant which inherits directly from BaseVariant, and ValueFromDerivedVariant which inherits from BaseVariant via DerivedVariant (which is empty but inherits the BaseVariant constructors):

#include <functional>
#include <tuple>
#include <type_traits>
#include <variant>

namespace maplibre {

template <typename T, typename... Types>
struct DirectType {
    static constexpr bool is_direct = (std::is_same_v<T, Types> || ...);
};

template <typename T, typename... Types>
struct ConvertibleType {
    static constexpr bool is_convertible = (std::is_convertible<T, Types>::value || ...);
};

template <typename T, typename... Types>
struct ValueTraits {
    using ValueType = typename std::remove_const<typename std::remove_reference<T>::type>::type;
    static constexpr bool is_valid = DirectType<ValueType, Types...>::is_direct || ConvertibleType<ValueType, Types...>::is_convertible;
};

template <typename... Types>
class variant : public std::variant<Types...> {
private:
    using base_variant = std::variant<Types...>;
    using current_variant = variant<Types...>;

public:
    variant() : base_variant() {}

    template <typename T,
              typename CurrentTraits = ValueTraits<T, Types...>,
              typename Enabled = typename std::enable_if<CurrentTraits::is_valid && !std::is_same_v<current_variant, typename CurrentTraits::ValueType>>::type>
    variant(T&& val) : base_variant(std::forward<T>(val)) {}
};
} // namespace maplibre


using BaseVariant = maplibre::variant<bool, int>;

class DerivedVariant : private BaseVariant {
  public:
    using BaseVariant::BaseVariant;
};


// Works for all compilers
class ValueFromBaseVariant : public BaseVariant
{
public:
    ValueFromBaseVariant(bool b) : BaseVariant(b) {}
};

// Works for gcc/clang, error for MSVC
class ValueFromDerivedVariant : public DerivedVariant
{
public:
    ValueFromDerivedVariant(bool b) : DerivedVariant(b) {}
};


On all compilers I've tried (clang, gcc, msvc), ValueFromBaseVariant compiles ok. But ValueFromDerivedVariant only compiles on clang and gcc. With msvc it gives the following error:

example.cpp
<source>(60): error C2665: 'DerivedVariant::DerivedVariant': no overloaded function could convert all the argument types
<source>(46): note: could be 'DerivedVariant::DerivedVariant(DerivedVariant &&)'
<source>(60): note: 'DerivedVariant::DerivedVariant(DerivedVariant &&)': cannot convert argument 1 from 'bool' to 'DerivedVariant &&'
<source>(60): note: Reason: cannot convert from 'bool' to 'DerivedVariant'
<source>(46): note: or       'DerivedVariant::DerivedVariant(const DerivedVariant &)'
<source>(60): note: 'DerivedVariant::DerivedVariant(const DerivedVariant &)': cannot convert argument 1 from 'bool' to 'const DerivedVariant &'
<source>(60): note: Reason: cannot convert from 'bool' to 'const DerivedVariant'
<source>(46): note: or       'DerivedVariant::DerivedVariant(void)'
<source>(60): note: 'DerivedVariant::DerivedVariant': function does not take 1 arguments
<source>(45): note: or       'DerivedVariant::DerivedVariant(T &&)', which inherits 'maplibre::variant<bool,int>::variant(T &&)' via base class 'maplibre::variant<bool,int>'
<source>(60): note: 'DerivedVariant::DerivedVariant(T &&)': could not deduce template argument for 'CurrentTraits'
<source>(60): note: 'Types': parameter pack cannot be used in this context
<source>(60): note: while trying to match the argument list '(bool)'
Compiler returned: 2

Godbolt links: msvc: https://godbolt.org/z/sv99qnn9P gcc: https://godbolt.org/z/5soz1oMeh

Have I done something wrong and gcc/clang are being lenient, or is msvc missing something? Thanks


Solution

  • The program is well-formed. This seems to be a msvc bug.

    MSVC rejects valid program involving using using declaration to inherit constructor