17

I have a function whose signature is:

void func(std::optional<std::string> os = std::nullopt);

(I’m aliasing std::experimental::optional until std::optional is officially available.)

However, I’m having difficulty calling it cleanly. The compiler will refuse to perform two implicit conversions (const char*std::stringstd::optional<std::string>) to call it with a raw C-string literal. I can do this:

func(std::string("Hello"));

And the compiler will figure that a std::optional is needed, and do the conversion. However, this is way too verbose. Thanks to C++11, I can also do this:

func({"Hello"});

While this is way better, it's still not ideal. I'd like to be able to call this function like any other that takes a std::string. Is this possible? Making the function take another parameter type is okay, as long as it behaves similarly to/is directly convertible to std::optional. Thanks.

Nissa
  • 4,636
  • 8
  • 29
  • 37
ThatsJustCheesy
  • 1,370
  • 14
  • 24
  • 1
    What's wrong with simply overloading the function, with no parameters and a `std::string` parameter? – Sam Varshavchik Feb 06 '17 at 00:42
  • @SamVarshavchik As far as I know, that would require three overloads to avoid mass-duplication: `void func(std::optional os = std::nullopt)`, `void func(std::string s)` and `void func()`. The latter two would call the former, constructing the optional as appropriate. I’d be happy if you could prove me wrong. While this does technically work, there’s just a lot of unnecessary interface code. Thanks, though. – ThatsJustCheesy Feb 06 '17 at 00:51
  • 1
    Well, is `func("std::string literal"s);` okay? – DeiDei Feb 06 '17 at 00:53
  • Huh… I never knew that existed. That might be the best solution. If you add it as a proper answer, I’ll happily accept it if nothing better comes along. Reference for others: http://en.cppreference.com/w/cpp/string/basic_string/operator%22%22s – ThatsJustCheesy Feb 06 '17 at 00:58
  • @ThatsJustCheesy, why would you need a third overload `func()` if there's a default argument for `func(std::optional = std::nullopt)`? – Jonathan Wakely Feb 06 '17 at 01:47
  • N.B. the error is a defect in `std::experimental::optional`, see [LWG DR 2451](http://cplusplus.github.io/LWG/lwg-defects.html#2451). There's no error with `std::optional`, nor with GCC's `std::experimental::optional`, but not all implementations have the fix for the DR. If you were really using `std::optional` and not faking it with `std::experimental::optional` there would be no error. – Jonathan Wakely Feb 06 '17 at 01:56
  • @JonathanWakely That's… a good point. Oops. So, I suppose one overload taking a std::string that forwards to the main one wouldn't be that bad. As for the defect report, it's good to know that this'll work better once the libc++ people fix this (see https://libcxx.llvm.org/cxx1z_status.html). – ThatsJustCheesy Feb 06 '17 at 20:27

2 Answers2

16

C++14 adds a bunch of user-defined literals to the standard library in order to make code less verbose. It looks something like this:

using namespace std::string_literals;              // needed
// using namespace std::literals;                  // also ok, but unnecessary 
// using namespace std::literals::string_literals; // also ok, but why??

int main()
{
    std::string str = "string"s;
                       ^^^^^^^^
                       // This is a std::string literal, 
                       // so std::string's copy constructor is called in this case
}

Also take a look at this and this for reference.

DeiDei
  • 10,205
  • 6
  • 55
  • 80
8

You can do that with a bit of templates and sfinae:

template<typename T, std::enable_if_t<
    std::is_constructible<std::string, T>::value &&
    !std::is_constructible<std::optional<std::string>, T>::value>* = nullptr>
void func(T&& s) {
    void func(std::string(std::forward<T>(s)));
}

This overload will be picked when a string would be constructible with a forwarded T but only when std::optional<std::string> is not constructible.

You function will be callable with any object that a string can be constructed with:

func("potato"); // working, forward the string literal to a std::string
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
  • I’d likely use this approach if I were writing a library that should be easy-to-use for the end-user. The other answer is better for my case, but this is a great alternative. Thanks a bunch. – ThatsJustCheesy Feb 06 '17 at 01:31