Błąd związany ze specjalizcją std::forward w MSVC 19.x nie występujący w innych kompilatorach.

0

Portuje pewien kod z linuksa na windowsa i natknąłem się na ciekawą rzecz. Mam zestaw helperów opakowujących typy z biblioteki napisanej w C w unique_ptr. W uproszczeniu te helpery funkcjonalnie sprowadzają się do tego:

struct Type {};
void deleter(Type* t) {if(t) delete t;}

template<>
inline auto make_unique(Type *raw) -> std::unique_ptr<Type, decltype(&deleter)>
{
    return std::unique_ptr<Type, decltype(&deleter)> (std::forward<Type*>(raw),
                                                      std::forward<decltype(&deleter)>(deleter));
}

No i okej, std::forward wstawiony bardziej z rozpędu niż z potrzeby bo zawsze dostanie lvalue. Ale ktoś tak napisał i w sumie nikomu nie przeszkadza. Ani clang ani gcc nie protestuje.

Tymczasem VC++ 2015
screenshot-20170411191755.png

no instance of overloaded function "std::forward" matches the argument list
argument types are: (void(Type *t))

Czy byłby ktoś tak miły i mi wytłumaczył dlaczego VC++ nie może sobie wyspecjalizować std::forward dla deletera? Bo po całym dniu walki z linkerem to już sam nie wiem, czy psikusa robi mi VC++ czy problem leży między klawiaturą a krzesłem.

0

Ale po co forwardowac wskaznik do funkcji?

0

Napisałem w poście, że wiem, że to forwardowanie jest nadmiariowe i wiem jak to poprawić. Zastanawiam się dlaczego na dwóch kompilatorach kompiluje się to bez warninga, a MSVC rzuca błędęm kompilacji.

1

Kod minimalny:

#include <utility>
using namespace std;

void deleter()
{
}

int main()
{
    forward<decltype(&deleter)>(deleter);   
}

Rzeczywiście, pod GCC się kompiluje a pod Visualem nie.
declspec nie jest winny, bo coś takiego:

        forward<void(*)()>(deleter);

skutkuje tym samym błędem.

Nie przywiązywałbym do problemu nadmiernej uwagi, ten forward i tak jest niepotrzebny ;-)

PS. ale po co takie make_unique, skoro w standardzie już jest funkcja o tej nazwie?

PPS. a dlaczego nie forward<decltype(deleter)>(deleter);, bez ampersandu?

0

PS. ale po co takie make_unique, skoro w standardzie już jest funkcja o tej nazwie?

Z dwóch powodów. To "legacy code" pisany przed wyjściem C++14. No i make_unique przykrywa new a biblioteka, której typy wrzucam do unique_ptr ma swoje własne inicjalizatory, które przykrywają malloc. Tak wiec te helpery są po to, żeby móc pisać

auto ptr = libwrap::make_unique(LIB_type_malloc());

Zamiast za każdym razem

auto ptr = std::unique_ptr<LIB_TYPE, decltype(&LIB_TYPE_free)>(LIB_type_malloc(), LIB_type_free);

PPS. a dlaczego nie forward<decltype(deleter)>(deleter);, bez ampersandu?

A to akurat jest bardzo dobre pytanie. Deleter musi być przekazany do unique_ptr jako wskaźnik na funkcję także musi być użyta wersja z ampersandem, dlatego założyłem że forward powinien dostać to samo. Mogłem się mylić.

Ciekawa zagwozdka, ale chyba na razie sobie odpuszczę, std::forward nie powinno tu być.

0

FYI to powinno wyglądać tak:

struct Type {};

struct DeleterOfType
{
    void operator()(Type* t)
    {
        delete t;
    }
};

template<>
inline auto make_unique(Type *raw) -> std::unique_ptr<Type, DeleterOfType>
{
    return std::unique_ptr<Type, DeleterOfType> (raw);
}

Twoja deklaracja powoduje, że pointer składa się z właściwego wskaźnika i wskaźnika na funkcję deleter.
W mojej implementacji funkcja deleter jest dowiązana statycznie do smart pointer-a.
Powinno działać pod VS (coś podobnego robiłem pod clang gcc i VS2013).

Swoją drogą po kiego grzyba ci ten forward? forward ma sens tylko dla klas, a nie typów prostych jak wskaźniki.

0

Przy nazwie funkcji powinien być ampersand:

struct Type {};
void deleter(Type* t) {if(t) delete t;}

template<>
inline auto make_unique(Type *raw) -> std::unique_ptr<Type, decltype(&deleter)>
{
    return std::unique_ptr<Type, decltype(&deleter)> (std::forward<Type*>(raw),
                                                      std::forward<decltype(&deleter)>(/*-->*/&/*<--*/deleter)); // <------ TUTAJ
}

Wtedy się kompiluje :-) O ile dobrze pamiętam, to jest to tutaj konieczne.

0

Bazując na uproszczonym przykładzie od @Azarien i na tym co napisał @mwl4

forward<decltype(&deleter)>(deleter);

W MSVC nie działa, bo z jakiegoś powodu forward nie rozpoznaje deleter jako wskaźnika do funkcji i trzeba mu jawnie o tym "powiedzieć" pisząc tak

forward<decltype(&deleter)>(&deleter);

O ile dobrze pamiętam, to jest to tutaj konieczne.

Tylko dlaczego? Przecież obydwa poniższe wywołania bar

void func(){};
void bar(void (*funcPtr)()) {}

bar(func);
bar(&func);

Są sobie równoważne w C++. Dlaczego forward w MCVC potrzebue tego ampersanda a unique_ptr (patrz niżej) gdy przekazujesz deleter już nie? Tak wiele pytań.

auto ptr = std::unique_ptr<int, decltype(&deleter)>(nullptr, deleter); // all good bro, no & needed

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0