0

You might say that I should be fine with

std::is_lvalue_reference<T>::value 

But the problem appears when string literals come in a question. As what I have read, string literals are treated as const char*, which is evaluated as an lvalue. How to ensure that a string literal will be treated as an rvalue?

Assume following non-existing code.

template <typename T>
inline static void f(T&& arg) {
    static_assert(my_lvalue_traits<T>::value, "Only lvalue permitted") ;
   // Some code.
}

How should my_lvalue_traits look like if i want to achieve these results?

std::string x;
f(x); // Everything OK, the argument is an lvalue. 

int y;
f(y); // Everything OK, the argument is an lvalue.

f("some literal"); // Static assertion failure. I want string literal to behave as an rvalue. 

f(5); // Static assertion failure. Argument is an rvalue.

Please note that I want to use this traits in a variadic templated class, so this might not be a perfect example. Something like

f(std::string("some literal")); 

is not a solution, too.

Speedding
  • 139
  • 1
  • 1
  • 6
  • 4
    String literal **is** an lvalue, your question is wrong. Maybe you want "a trait that accepts lvalue but excludes string literal". This is impossible, because after a couple of forwarding, there is no information to tell whether the original is from a string literal. – llllllllll Apr 02 '18 at 15:03
  • 1
    You should move your requirement that 'string literal be treated as rvalue', from the code comments to your question. – smac89 Apr 02 '18 at 15:05
  • Just to be perfectly explicit, you're asking for `f("some literal");` to fail, for `const char (&str)[13] = "some literal"; f(str);` to succeed, even though `decltype("some literal")` and `decltype(str)` are identical? –  Apr 02 '18 at 15:27

2 Answers2

1

A string literal is an lvalue. So you can't just treat string literals as rvalues, and that's not really what you want anyway - you want to exclude string literals.

One way to do that is to just delete the rvalue const char* overload:

template <typename T>
void foo(T&& ) {
    static_assert(std::is_lvalue_reference_v<T>);
}

void foo(const char*&& ) = delete;

Calling foo("wat") prefers the second (see this question), so this effectively excludes them (while the string literal itself is an lvalue, the pointer it decays into is an rvalue - so it can bind to an rvalue reference).

However, note that this also effectively excludes any other character array.

foo("hello"); // ill-formed, as desired

const char msg[] = "hello";
foo(msg); // also ill-formed, unfortunately

There's really no way around that, since you cannot differentiate (edit: at the point of substitution during template deduction) between a string literal and any other kind of character array.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • I like this. First time seeing the `delete` applied to free functions – smac89 Apr 02 '18 at 15:29
  • Actually, your answer gives one of the few cases where you *can* perhaps differentiate between the two: string literals can be used as array initialisers, other arrays cannot. You can't have declarations in substitution context though, so I don't see how this might be abused to do what the OP asks, but I'm not ready to say it's impossible yet. –  Apr 02 '18 at 15:33
  • @hvd Right, you can't differentiate in a way that would be useful for this particular problem (edited that clarification in). And I've definitely wanted to before... – Barry Apr 02 '18 at 15:35
  • @Barry Possible starting point: `struct non_array_wrapper { const char *ptr; };`, `struct array_wrapper { const char array[4]; };`, `char f(void *, non_array_wrapper);`, `int f(int, array_wrapper);`. Now, `decltype(f(0, {"abc"}))` is `int`, but `decltype(f(0, {*&"abc"}))` is `char`. –  Apr 02 '18 at 16:06
0

A string literal is not quite a const char *. You can take advantage of this using specialization.

Tested with gcc 7.3.1:

#include <type_traits>
#include <utility>
#include <functional>

template<typename T>
inline static void f(T&& arg) {
    static_assert(std::is_lvalue_reference_v<T>, "Only lvalue permitted");
}

template<size_t n>
inline static void f(const char (&arg)[n])
{
    static_assert(n!=n, "Only lvalue permitted");
}

int main()
{
    std::string x;

    f(x); // ok

    int y;

    f(y); // ok

    const char *p;

    f(p); // ok

    f("foobar"); // fail

    return 0;
}
Sam Varshavchik
  • 114,536
  • 5
  • 94
  • 148
  • 3
    This assumes all `const char [N]` lvalues should be excluded rather than just string literals. That's not what the question asks. –  Apr 02 '18 at 15:11
  • Also, this is ill-formed (NDR). – Barry Apr 02 '18 at 15:25