Forwarding reference which binds to only one type?

Viewed 100

I want to have a forwarding reference which only binds to one type. I can use a static assert, but I wonder if there is a more elegant (C++14/17) way to do this.

Here an example:

#include <iostream>
#include <string>
#include <type_traits>

template<class T> 
void f(T&& x) {
    // Is there a more elegant way to check for the type than the static_assert?
    static_assert(std::is_same<std::string, typename std::remove_cv_t<std::remove_reference_t<T>>>::value);
    x = x + "Bar";   
    std::cout << x << std::endl;
}

int main()
{
  std::string name("Foo");
  f(name);
  f(std::move(name));
}
3 Answers

You can use compile-time check using std::enable_if, as follows

template<class T>
std::enable_if_t<std::is_same_v<std::string, typename std::remove_cv_t<std::remove_reference_t<T>>>, void> f(T&& x) {
    x = x + "Bar";
    std::cout << x << std::endl;
}

If u can use c++20, this will be nicer, as follows

template<class T>
void f(T&& x)requires(std::is_same_v<std::string, typename std::remove_cv_t<std::remove_reference_t<T>>>) {
    x = x + "Bar";
    std::cout << x << std::endl;
}

C++20 way:

template <typename T>
requires std::same_as<std::string, std::remove_cvref_t<T>>
void f(T &&x) {}

C++17 way:

template <
    typename T,
    std::enable_if_t<
        std::is_same_v<
            std::string,
            std::remove_cv_t<std::remove_reference_t<T>>
        >,
        std::nullptr_t
    > = nullptr
>
void f(T &&x) {}

The C++14 way is same as C++17, except you have to use std::same_as<...>::value instead of std::is_same_v.

I’ve found myself in this problem too and didn’t have a solution until c++20, which is still not perfect.

template<typename T>
concept StringRef = std::is_same<std::string, typename std::remove_cv_t<std::remove_reference_t<T>>>::value;

void f(StringRef auto &&x)
{
    // ...
}
Related