12

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

Ami Tavory
  • 74,578
  • 11
  • 141
  • 185
xinaiz
  • 7,744
  • 6
  • 34
  • 78

2 Answers2

9

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

The (short) answer is yes.


As an example, you can use the following specialization in your example code:

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

That quickly becomes:

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

The output is the expected one: 0 for all of them.


Another way to do that in C++14 (mostly inspired by this answer of @Jarod42) 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;
}

I find it easy to read and that's a constexpr variable. What else?

Community
  • 1
  • 1
skypjack
  • 49,335
  • 19
  • 95
  • 187
  • Note this solution doesn't quite work if operator is defined after the type trait is defined in GCC at least: http://coliru.stacked-crooked.com/a/6f70eb6cbf236473 – xinaiz Sep 29 '16 at 22:31
5

With is_detected functions family, you may just do

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

And test it with

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

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • 1
    Shouldn't it be `std::declval()` instead of `std::declval`? – skypjack Sep 27 '16 at 13:55
  • 1
    @skypjack: Sure, fixed, thanks. I didn't want to add the valid case in Demo as OP expected that the operator may not appears, so I miss that typo... – Jarod42 Sep 27 '16 at 13:58
  • 1
    Well, this is beast! Great solution in general terms, but I expected any working solution, and skypjack was first this time and did it with generally known syntax :) Although `is_detected` would be The Winner of question "How to simplify complicated SFINAE syntax". – xinaiz Sep 27 '16 at 14:05
  • @BlackMoses I added another snippet to my answer with a solution based on a template variable, that is mostly inspired by this one. Can I participate to the _The Winner of Question "How to simplify complicated SFINAE syntax"_ contest? :-) – skypjack Sep 27 '16 at 14:09
  • 1
    @skypjack Here you go :) http://stackoverflow.com/questions/39727409/how-to-simplify-complicated-sfinae-syntax-in-pre-c11-c11-14-and-17 – xinaiz Sep 27 '16 at 14:31