在模板参数中评估constexpr时,SFINAE是否失败? [英] SFINAE failing when evaluating a constexpr in a template parameter?

查看:57
本文介绍了在模板参数中评估constexpr时,SFINAE是否失败?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

For some reason, this constexpr is not being evaluated correctly in a template parameter context:

#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);
}

我已经使用 g ++ vc ++ 。每个都有不同的错误:

I've tested this with clang, g++ and vc++. Each had different errors:

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 ++



g++

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



vc ++



vc++

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

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

Is my code invalid or are the compilers just not up to the job yet?

推荐答案

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

Your code is invalid. The compiler (GCC7.1 for me) provides helpful error that allows us to solve this problem.

问题:

...\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<> 可能会执行动态内存分配(除其他原因外),因此这并非易事。您必须将其替换为可破坏的类型。我在下面提供一个简单的解决方案。

The reason detail::is_value does not have a trivial destructor is due to the std::function<> member; std::function<> might perform dynamic memory allocation (among other reasons), thus it is not trivial. You have to replace it with a trivially destructible type; I present a simple solution below.

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

Note: Even if std::function<> was trivially destructible, its operator() does not seem to be declared as constexpr (see: http://en.cppreference.com/w/cpp/utility/functional/function/operator()), so it would not work either.

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

Sample working code (adapt as needed):

#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);
}

这篇关于在模板参数中评估constexpr时,SFINAE是否失败?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆