是否可以检查是否为给定类型和参数定义了用户文字?

Is it possible to check if a user literal is defined for given type and argument?

我想在编译时检查是否为类型 Ret 和参数 Arg 定义了用户文字 _name。虽然我有一半的解决方案,但它要求文字 operator 至少定义一次:

#include <iostream>
#include <type_traits>

struct one { };
struct two { };

// we need at least one of these definitions for template below to compile
one operator"" _x(char const*) {return {};}
two operator"" _x(unsigned long long int) {return {};}

template<class T, class S, class = void>
struct has_literal_x : std::false_type
{  };

template<class T, class S>
struct has_literal_x <T, S,
    std::void_t<decltype((T(*)(S))(operator"" _x))>
    > : std::true_type
{ };

int main()
{
    std::cout << has_literal_x<one, char const*>::value << std::endl;
    std::cout << has_literal_x<two, unsigned long long int>::value << std::endl;

    std::cout << has_literal_x<one, unsigned long long int>::value << std::endl;
    std::cout << has_literal_x<two, char const*>::value << std::endl;

    std::cout << has_literal_x<int, char const*>::value << std::endl;
}

输出:

1
1
0
0
0

但如果没有至少一个可能重载的用户文字定义,则此解决方案将不起作用。有没有办法检查它甚至不存在文字(可能与我们检查 class X 是否有成员 member 的方法相同,但我不知道它是否可行这种情况)?

Is it possible to check if an user literal is defined for given type and argument?

(简短的)答案是


例如,您可以在示例代码中使用以下特化:

template<class T, class S> 
struct has_literal_x <T, S,
      std::enable_if_t<std::is_same<decltype(operator""_x(std::declval<S>())), T>::value>
    > : std::true_type
{ };

那很快就变成了:

#include <iostream>
#include <type_traits>
#include <utility>

struct one { };
struct two { };

//one operator"" _x(char const*) { return {}; }
//two operator"" _x(unsigned long long int) { return {}; }

template<class T, class S, class = void>
struct has_literal_x : std::false_type
{  };

template<class T, class S> 
struct has_literal_x <T, S, 
      std::enable_if_t<std::is_same<decltype(operator""_x(std::declval<S>())), T>::value> 
    > : std::true_type
{ };

int main()
{  
    std::cout << has_literal_x<one, char const*>::value << std::endl;
    std::cout << has_literal_x<two, unsigned long long int>::value << std::endl;

    std::cout << has_literal_x<one, unsigned long long int>::value << std::endl;
    std::cout << has_literal_x<two, char const*>::value << std::endl;

    std::cout << has_literal_x<int, char const*>::value << std::endl;
}

输出是预期的输出:0 所有这些。


C++14 中的另一种方法(主要受@Jarod42 的 回答启发)是通过模板变量。
例如:

template<typename T, typename S, typename = void>
constexpr bool has_literal_v = false;

template<typename T, typename S>
constexpr bool has_literal_v<T, S, std::enable_if_t<std::is_same<decltype(operator""_x(std::declval<S>())), T>::value>> = true;

main 会变成:

int main()
{  
    std::cout << has_literal_v<one, char const*> << std::endl;
    std::cout << has_literal_v<two, unsigned long long int> << std::endl;

    std::cout << has_literal_v<one, unsigned long long int> << std::endl;
    std::cout << has_literal_v<two, char const*> << std::endl;

    std::cout << has_literal_v<int, char const*> << std::endl;
}

我觉得它很容易阅读,这是一个 constexpr 变量。还有什么?

使用 函数系列,您可以做到

template <typename T>
using has_literal_x_type = decltype(operator"" _x(std::declval<T>()));

template <typename Ret, typename T>
using has_literal_x = std::is_same<Ret, detected_t<has_literal_x_type, T>>;

并使用

进行测试
static_assert(!has_literal_x<one, char const*>::value, "unexpected");
static_assert(!has_literal_x<one, unsigned long long int>::value, "unexpected");
static_assert(!has_literal_x<two, char const*>::value, "unexpected");
static_assert(!has_literal_x<two, unsigned long long int>::value, "unexpected");
static_assert(!has_literal_x<int, char const*>::value, "unexpected");

Demo