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.

7
  • 1
    What's wrong with simply overloading the function, with no parameters and a std::string parameter? Commented Feb 6, 2017 at 0:42
  • @SamVarshavchik As far as I know, that would require three overloads to avoid mass-duplication: void func(std::optional<std::string> 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. Commented Feb 6, 2017 at 0:51
  • 1
    Well, is func("std::string literal"s); okay? Commented Feb 6, 2017 at 0: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: en.cppreference.com/w/cpp/string/basic_string/operator%22%22s Commented Feb 6, 2017 at 0:58
  • @ThatsJustCheesy, why would you need a third overload func() if there's a default argument for func(std::optional<std::string> = std::nullopt)? Commented Feb 6, 2017 at 1:47

2 Answers 2

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.

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
1
  • 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. Commented Feb 6, 2017 at 1:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.