c++ - SFINAE 在评估模板参数中的 constexpr 时失败?

标签 c++ c++11 visual-c++ c++14 sfinae

出于某种原因,此 constexpr 在模板参数上下文中未被正确评估:

#include <iostream>
#include <functional>

namespace detail
{
    // Reason to use an enum class rahter than just an int is so as to ensure
    // there will not be any clashes resulting in an ambigious overload.
    enum class enabler
    {
        enabled
    };
}
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), detail::enabler> = detail::enabler::enabled
#define ENABLE_IF_DEFINITION(...) std::enable_if_t<(__VA_ARGS__), detail::enabler>

namespace detail
{
    template <typename T, bool IS_BUILTIN>
    class is_value
    {
        T item_to_find;
        std::function<bool(T const& lhs, T const& rhs)> predicate;
    public:
        constexpr is_value(T item_to_find, std::function<bool(T, T)> predicate)
            : item_to_find(item_to_find)
            , predicate(predicate)
        {}

        constexpr bool one_of() const
        {
            return false;
        }

        template <typename T1, typename...Ts>
        constexpr bool one_of(T1 const & item, Ts const&...args) const
        {
            return predicate(item_to_find, item) ? true : one_of(args...);
        }
    };

    template <typename T>
    class is_value<T, false>
    {
        T const& item_to_find;
        std::function<bool(T const& lhs, T const& rhs)> predicate;
    public:
        constexpr is_value(T const& item_to_find, std::function<bool(T const&, T const&)> predicate)
            : item_to_find(item_to_find)
            , predicate(predicate)
        {}

        constexpr bool one_of() const
        {
            return false;
        }

        template <typename T1, typename...Ts>
        constexpr bool one_of(T1 const & item, Ts const&...args) const
        {
            return predicate(item_to_find, item) ? true : one_of(args...);
        }
    };
}

// Find if a value is one of one of the values in the variadic parameter list.
// There is one overload for builtin types and one for classes.  This is so
// that you can use builtins for template parameters.
//
// Complexity is O(n).
//
// Usage:
//
//   if (is_value(1).one_of(3, 2, 1)) { /* do something */ }
//
template <typename T, ENABLE_IF(!std::is_class<T>::value)>
constexpr auto const is_value(T item_to_find, std::function<bool(T, T)> predicate = [](T lhs, T rhs) { return lhs == rhs; })
{
    return detail::is_value<T, true>(item_to_find, predicate);
}

template <typename T, ENABLE_IF(std::is_class<T>::value)>
constexpr auto const is_value(T const& item_to_find, std::function<bool(T const&, T const&)> predicate = [](T const& lhs, T const& rhs) { return lhs == rhs; })
{
    return detail::is_value<T, false>(item_to_find, predicate);
}


template <int I, ENABLE_IF(is_value(I).one_of(3,2,1))>
    void fn()
{
}

int main()
{
    fn<3>();
    std::cout << "Hello, world!\n" << is_value(3).one_of(3,2,1);
}

我用 clang 测试过这个, g++vc++ .每个都有不同的错误:

clang

source_file.cpp:98:5: error: no matching function for call to 'fn'
    fn<3>();
    ^~~~~
source_file.cpp:92:10: note: candidate template ignored: substitution failure [with I = 3]: non-type template argument is not a constant expression
    void fn()
         ^
1 error generated.

g++

source_file.cpp: In function ‘int main()’:
source_file.cpp:98:11: error: no matching function for call to ‘fn()’
     fn<3>();
...

VC++

source_file.cpp(91): fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'msc1.cpp', line 1421)
...

是我的代码无效还是编译器还不能胜任工作?

最佳答案

您的代码无效。编译器(对我来说是 GCC7.1)提供了有用的错误,使我们能够解决这个问题。

问题:

...\main.cpp|19|note: 'detail::is_value<int, true>' is not literal because:|
...\main.cpp|19|note:   'detail::is_value<int, true>' has a non-trivial destructor|

原因detail::is_value没有平凡的析构函数是由于 std::function<>成员; std::function<>可能会执行动态内存分配(以及其他原因),因此它并不简单。你必须用一个普通的可破坏类型来替换它;我在下面提出了一个简单的解决方案。

注意:即使std::function<>很容易被破坏,它的operator()似乎没有声明为 constexpr (参见:http://en.cppreference.com/w/cpp/utility/functional/function/operator()),所以它也不起作用。

示例工作代码(根据需要进行调整):

#include <iostream>
#include <functional>

namespace detail
{
    // Reason to use an enum class rahter than just an int is so as to ensure
    // there will not be any clashes resulting in an ambigious overload.
    enum class enabler
    {
        enabled
    };
}
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), detail::enabler> = detail::enabler::enabled
#define ENABLE_IF_DEFINITION(...) std::enable_if_t<(__VA_ARGS__), detail::enabler>

namespace detail
{
    // notice the new template parameter F
    template <typename T, typename F, bool IS_BUILTIN>
    class is_value
    {
        T item_to_find;
        F predicate;
    public:
        constexpr is_value(T item_to_find, F predicate)
            : item_to_find(item_to_find)
            , predicate(predicate)
        {}

        constexpr bool one_of() const
        {
            return false;
        }

        template <typename T1, typename...Ts>
        constexpr bool one_of(T1 const & item, Ts const&...args) const
        {
            return predicate(item_to_find, item) ? true : one_of(args...);
        }
    };

    template <typename T, typename F>
    class is_value<T, F, false>
    {
        T const& item_to_find;
        F predicate;
    public:
        constexpr is_value(T const& item_to_find, F predicate)
            : item_to_find(item_to_find)
            , predicate(predicate)
        {}

        constexpr bool one_of() const
        {
            return false;
        }

        template <typename T1, typename...Ts>
        constexpr bool one_of(T1 const& item, Ts const&... args) const
        {
            return predicate(item_to_find, item) ? true : one_of(args...);
        }
    };
}

// sample predicate
template<class T>
struct default_compare
{
    constexpr bool operator()(T const& lhs, T const& rhs) const
    noexcept(noexcept(std::declval<T const&>() == std::declval<T const&>()))
    {
        return lhs == rhs;
    }
};

// Find if a value is one of one of the values in the variadic parameter list.
// There is one overload for builtin types and one for classes.  This is so
// that you can use builtins for template parameters.
//
// Complexity is O(n).
//
// Usage:
//
//   if (is_value(1).one_of(3, 2, 1)) { /* do something */ }
//
template <typename T, typename F = default_compare<T>, ENABLE_IF(!std::is_class<T>::value)>
constexpr auto const is_value(T item_to_find, F predicate = {})
{
    return detail::is_value<T, F, true>(item_to_find, predicate);
}

template <typename T, typename F = default_compare<T>, ENABLE_IF(std::is_class<T>::value)>
constexpr auto const is_value(T const& item_to_find, F predicate = {})
{
    return detail::is_value<T, F, false>(item_to_find, predicate);
}

template <int I, ENABLE_IF(is_value(I).one_of(3,2,1))>
    void fn()
{
}

int main()
{
    fn<3>();
    std::cout << "Hello, world!\n" << is_value(3).one_of(3,2,1);
}

关于c++ - SFINAE 在评估模板参数中的 constexpr 时失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43948240/

相关文章:

c++ - 如何在 C++11 的构造函数初始化列表中使用一个公共(public)参数初始化元组的元素?

c++ - 将字符串从字符串流移动到字符串 vector 中

visual-studio - 让 Doxygen 和 MSVC TODO 标签协同工作

c++ - 如何增加 Visual Studio 中的错误限制?

c++ - 从子对话框访问变量

c++ - std::map clear() 在调试器中的性能?

java - 如何使用java在opencv中创建人脸模型?

C++ 类型的值不能分配给类型的实体

c++ - 计算多边形 union 时在 CGAL 中触发的断言

c++ - 递归生成给定子集大小的所有组合 (C++)