0

The following code can be compiled with g++ 8.1.0 and clang 10.0.0.

#include <memory>
#include <iostream>
#include <functional>

int main() {
    auto dereference = std::bind(
        &std::shared_ptr<int>::operator*,
        std::placeholders::_1);

    std::shared_ptr<int> sp = std::make_shared<int>(10);
    std::cout << dereference(sp) << std::endl;

    return 0;
}

However, msvc in vs2022 reports the following error:

example.cpp<source>(6): error C2672: 'std::bind': no matching overloaded function foundC:/data/msvc/14.34.31931-Pre/include\functional(2029): note: could be 'std::_Binder<_Ret,_Fx,_Types...> std::bind(_Fx &&,_Types &&...)'<source>(8): 
note: 'std::_Binder<_Ret,_Fx,_Types...> std::bind(_Fx &&,_Types &&...)': could not deduce template argument for '_Ret'<source>(8): note: 'std::_Binder<_Ret,_Fx,_Types...> std::bind(_Fx &&,_Types &&...)': could not deduce template argument for '_Fx'C:/data/msvc/14.34.31931-Pre/include\functional(2024): note: or       'std::_Binder<std::_Unforced,_Fx,_Types...> std::bind(_Fx &&,_Types &&...)'<source>(8): note: 'std::_Binder<std::_Unforced,_Fx,_Types...> std::bind(_Fx &&,_Types &&...)': could not deduce template argument for '_Fx'<source>(11): error C3536: 'dereference': cannot be used before it is initialized<source>(11): error C2064: term does not evaluate to a function taking 1 argumentsCompiler returned: 2

Is it a bug of msvc compiler or the above code can be fixed?

To reproduce the compile error, you can go to https://godbolt.org/ for details.

πάντα ῥεῖ
  • 1
  • 13
  • 116
  • 190
syby119
  • 43
  • 4

2 Answers2

2

Behavior is unspecified when trying to take the address of a member function of a standard library class. This allows the standard library to choose a different number or different declarations for the overloads of the member function, as long as direct calls still behave as specified by the standard. For example if the implementation chooses to implement operator* with multiple overloads, then taking the address for std::bind won't work since there is no way to deduce which of the overloads you mean.

Instead of std::bind use a lambda that doesn't require taking the address of a function. std::bind is pretty out-dated anyway and lambdas are much simpler to use:

auto dereference = [](auto&& p)->decltype(auto){ return *decltype(p)(p); };

This works for any type and also does perfect-forwarding correctly. If you don't want that you can specify a type explicitly instead of auto and you can replace decltype(p)(p) with p if you don't want perfect-forwarding. You might also want to add a noexcept specification.

->decltype(auto) makes it so that the value category for the function call is also replicated exactly, meaning that if operator* returns a reference, so will the lambda. Without it the lambda will always return by-value. Which one you want depends on the purpose of this callable.

user17732522
  • 53,019
  • 2
  • 56
  • 105
1

Taking the address of a function in standard library apart. You should not do this

Assume that you can safely take the address. (which is not).

In MSVC, this function member is overloaded for reasons. You cannot take the address of a overloaded function, unless, roughly, a conversion sequence is formed one way or another. Unfortunately, this is not the case when calling std::bind.

You can verify if a function member is overloaded by trying

auto addr = &std::shared_ptr<int>::operator*;

A way to get the address of a overloaded member is to cast it explicitly yourself(specify which overload you would like to refer). static_cast<int &(std::shared_ptr<int>::*)() const noexcept>(&std::shared_ptr<int>::operator*). As member std::shared_ptr<int>::operator* should have a signature T& operator*() const noexcept according to the standard.

Not a fix:

#include <memory>
#include <iostream>
#include <functional>

int main() {
    auto dereference = std::bind(
        static_cast<int &(std::shared_ptr<int>::*)() const noexcept>(&std::shared_ptr<int>::operator*),
        std::placeholders::_1);

    std::shared_ptr<int> sp = std::make_shared<int>(10);
    std::cout << dereference(sp) << std::endl;

    return 0;
}
felix
  • 2,213
  • 7
  • 16