3

I'm writing some container class that wraps std::map. A simplified version if it is:

#include <map>

template <typename key_type, typename value_type>
class map2 : private std::map<key_type, value_type>
{
public:
    void update(const key_type& key, value_type value)
    {
        (*this)[key] = std::move(value);
    }
};

int main()
{
    map2<int, int> m;
    m.update(1,4);
}

This code compiles just fine on gcc and clang, but on Visual C++ (I tested the 2015 version and whatever is used on http://rextester.com/l/cpp_online_compiler_visual ) it fails with:

source_file.cpp(16): error C2664: 'void map2<int,int>::update(const int &,std::pair<const _Kty,_Ty>)': cannot convert argument 2 from 'int' to 'std::pair<const _Kty,_Ty>'
        with
        [
            _Kty=int,
            _Ty=int
        ]
source_file.cpp(16): note: No constructor could take the source type, or constructor overload resolution was ambiguous
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64

So Visual C++ somehow assumes that the value parameter of map2::update() is of type std::pair<key_type, value_type> and not of value_type. But why is it doing that, while gcc and clang accept my code just fine?

Dreamer
  • 1,139
  • 9
  • 18
  • 4
    I don't know why VC++ is doing it (although that compiler's name lookup rules have always been screwy), but the simple solution is not to name your template parameters the same as the typedefs from the base class. – Sebastian Redl Mar 01 '18 at 13:01
  • fyi: this post addresses inheriting from `std::vector`, but I think most of it applies to `std::map` as well: https://stackoverflow.com/questions/4353203/thou-shalt-not-inherit-from-stdvector – Richard Critten Mar 01 '18 at 13:09
  • This is fixed by setting the `/permissive-` command line switch – Mgetz Mar 01 '18 at 15:00
  • @Mgetz that's an option if you have control over the build environment. But for CI builds you're generally stuck with the compiler flags somebody else has set, and need to adjust your code to compile with those flags. So setting a command line switch is not a general solution to this problem. – Dreamer Mar 02 '18 at 13:32
  • @Dreamer true, I would suggest convincing them to go with `/permissive-` anyway as it helps enforce conformance with the standard. – Mgetz Mar 02 '18 at 14:18

1 Answers1

3

The problem is likely what Visual C++ is not doing, and that's two-phase name lookup. Since there's only one phase of name lookup, it will have to look up value_type in that phase. And as a hack, VC++ then already looks into dependent base classes such as std::map<key_type, value_type> here.

This incorrectly finds std::map<K, V>::value_type, a typedef for pair<K,V>. This explains the VC++ error message.

Workaround: Disambiguate the argument name in map2 yourself.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • Agreed - this sounds plausible. Suggest using completely different names. Probably a good idea anyway tbh – Lightness Races in Orbit Mar 01 '18 at 13:32
  • If this is the case then VS2017 should potentially compile this code in `/permissive-` and C++17 mode as that is supposed to allegedly support two phase. Edit [Confirmed](https://godbolt.org/g/V5gtdY) – Mgetz Mar 01 '18 at 14:25