Black Moses Black Moses - 2 months ago 5
C++ Question

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

I want to check at compile-time if user literal

_name
is defined for type
Ret
and argument
Arg
. While I have half-solution, it requires the literal
operator
to be defined at least once:

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


Output:

1
1
0
0
0


But if there isn't at least one definition of possibly overloaded user literal, this solution will not work. Is there any way to check it even for non-existing literals (possibly the same way we can check if class
X
has member
member
, but I don't know if it's viable in this case)?

Answer

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

The answer is yes.
As an example, you can use this specialization in your case:

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
{ };

Your (now working) example reviewed:

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

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::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;
}

The output is the one below as expected:

0
0
0
0
0


Another way to do that in C++14 (mostly inspired by this answer) is by means of a template variable.
As an example:

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;

The main would become instead:

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