在评估模板参数中的constexpr时,SFINAE失败了吗?

时间:2017-05-13 01:00:11

标签: 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);
}

我已使用clangg++vc++对此进行了测试。每个都有不同的错误:

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)
...

我的代码是无效的还是编译器还没达到工作呢?

1 个答案:

答案 0 :(得分:3)

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

问题:

detail::is_value

std::function<>没有简单的析构函数的原因是std::function<>成员; std::function<>可能执行动态内存分配(以及其他原因),因此它并非无足轻重。你必须用一种易于破坏的类型来代替它;我在下面提出一个简单的解决方案。

注意:即使operator()被轻易破坏,其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 { // 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); } (请参阅:http://en.cppreference.com/w/cpp/utility/functional/function/operator()),所以它也无效

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

{{1}}