VS выбирает неправильную специализацию?Зачем? - PullRequest
0 голосов
/ 06 декабря 2018

Вот некоторый код:

#include <iostream>
#include <functional>
#include <string>
#include <variant>

namespace detail
{
    std::string Foo(const double val)
    {
        return std::to_string(val);
    }
}

using Value = std::variant<std::monostate, double, int, bool, std::string>;
struct ReadStore {};

struct ValueException : std::runtime_error {};

using PrintConvType = std::function<Value(const Value&, ReadStore&)>;


template <typename InType, typename OutType, typename CallableType, CallableType Callable, bool Strict>
Value ConvWrapperImpl(const Value& value)
{
    if constexpr (std::is_same_v<InType, Value>)
    {
        return Callable(value);
    }
    else
    {
        if (value.index() != 0)
        {
            const auto* ptr = std::get_if<InType>(&value);
            if (ptr)
                return Callable(*ptr);
        }

        return value;
    }
}


template <typename T1, typename T2, T2 (*Callable)(const T1&), bool Strict = false>
Value ConvWrapper(const Value& value, ReadStore&)
{
    return ConvWrapperImpl<T1, T2, decltype(Callable), Callable, Strict>(value);
}

template <typename T1, typename T2, T2 (*Callable)(T1), bool Strict = false>
Value ConvWrapper(const Value& value, ReadStore&)
{
    return ConvWrapperImpl<T1, T2, decltype(Callable), Callable, Strict>(value);
}

int main()
{
    using namespace detail;

    ReadStore store;
    PrintConvType func = ConvWrapper<double, std::string, Foo>;

    Value result = func(3.14159, store);
     std::cout << std::get<std::string>(result) << '\n';
}

Это придуманный MCVE, но общая идея в оригинальном проекте состоит в том, чтобы обеспечить сокращение для преобразования преобразования Value в соответствующий тип аргумента для некоторого обратного вызова.функции и для преобразования преобразования возвращаемого типа указанной функции обратно в Value снова.

Это нормально при GCC и Clang, но мои ошибки Visual Studio 2017 (v15.7.2)out:

error C2440: 'specialization': cannot convert from 'std::string (__cdecl *)(const double)' to 'std::string (__cdecl *)(const double &)'
note: This conversion requires a reinterpret_cast, a C-style cast or a function-style cast
error C2973: 'convWrapper': invalid template argument 'std::string (__cdecl *)(const double)'
note: see declaration of 'ConvWrapper'

В зависимости от того, куда ссылается последняя заметка, создается впечатление, что она выбрана неверно ConvWrapper, а затем удивляется, когда подпись Callable не совпадает.

Что странно, что на Godbolt с тем же выбранным компилятором, код принят .

Может ли конфигурация повлиять на это?Это я?

Как это исправить?Или с изменением конфигурации или с изменением кода?

Ответы [ 2 ]

0 голосов
/ 06 декабря 2018

Вы можете избежать перегрузок с помощью c ++ 17, заменив typename CallableType, CallableType Callable на auto Callable.Тогда MSVC не нужно конвертировать между типами.

template <typename InType, auto Callable, bool Strict>
Value ConvWrapperImpl(const Value& value)
{
    if constexpr (std::is_same_v<InType, Value>)
    {
        return Callable(value);
    }
    else
    {
        if (value.index() != 0)
        {
            const auto* ptr = std::get_if<InType>(&value);
            if (ptr)
                return Callable(*ptr);
        }

        return value;
    }
}

template <typename T1, auto Callable, bool Strict = false>
Value ConvWrapper(const Value& value, ReadStore&)
{
    return ConvWrapperImpl<T1, Callable, Strict>(value);
}

с аналогичным использованием (ConvWrapper<double, &Foo> вместо ConvWrapper<double, std::string, &Foo>):

int main()
{
    using namespace detail;

    ReadStore store;
    PrintConvType func = ConvWrapper<double, &Foo>;

    Value result = func(3.14159, store);
     std::cout << std::get<std::string>(result) << '\n';
}

Демо

0 голосов
/ 06 декабря 2018

Флаг компилятора, который вызывает проблему, является флагом соответствия /permissive-.Если вы удалите его из команды компилятора или измените его в свойствах «Отладка» -> «имя проекта» -> C / C ++ -> «Язык» -> «Режим соответствия» на «Нет», это приведет к компиляции.

...