将除字符串以外的所有类型按值传递给模板函数

Passing by value all types but string to a template function

我想定义一个模板函数,它为除 std::string(和 const char*)之外的所有类型获取一个按值传递的参数。

template<typename T>
void foo( T value )
{
    // some code using value
}

std::string 版本的行为应与 template 版本完全相同,但其参数由 const& 传递。

在不复制 foo() 正文的情况下完成我想做的事情的最佳方法是什么?

我能想到的最好的方法是使用 value 代码包装在另一个函数中,然后在 foo() 的所有版本中调用它(template 版本和 std::string 重载)。还有别的办法吗?例如,是否可以从 std::string 重载中调用 template 版本?

编辑

我想知道的是避免各种特化和重载之间的代码重复的良好经验法则。什么是可以遵循的好模式?我应该为主体定义一个包装函数,然后从所有 overloads/specializations 中调用它,还是有其他方法?

简单的解决方案:为 std::string:

提供重载
void foo( std::string const &value ) {
    // some code using value
}

使用 std::enable_if + std::is_convertibale:

   template<typename T>
    typename std::enable_if<!std::is_convertible<T,std::string>::value>::type foo( T value )
    {
        // some code using value
    }

您可以定义类似类型特征的 class,它将 std::string 转换为 std::string& 并保留所有其他类型的类型:

template<class T>
struct helper {
    typedef T type;
};

template<>
struct helper<std::string> {
    typedef std::string& type; // or const std::string& type if you want
};

template<typename T>
void foo( typename helper<T>::type value, T value2 )
{
    value = value2;
}

int main()
{
    int a = 10;
    foo(a, 42);
    std::cout << a << std::endl; // prints 10

    std::string s = "abc";
    foo(s, std::string("def"));
    std::cout << s << std::endl; // prints def
}

完整示例:http://coliru.stacked-crooked.com/a/96cf78e6c4846172

UPD:如@PiotrSkotnicki 所述,只有一个参数会使类型推导失败。但是,我会保留答案,因为如果您确实有多个 T 类型的参数,或者如果您可以为 foo.

指定显式模板参数,它可能会有所帮助

UPD2:要解决类型推导问题,您可以添加另一个包装器:

template<typename T>
void foo_helper( typename helper<T>::type value )
{
    value = T();
}

template<typename T>
void foo(T& value)
{
    foo_helper<T>(value);
}

这仍然可能存在一些问题,所以这是否适用于您的用例,由您决定。

我认为您正在寻找的是 C++ 11 中的右值签名。

它很简单:

#include <iostream>
#include <string>

template<typename T>
void foo(T&& value)
{
    std::cout << "was passed by refernece:" << std::is_lvalue_reference<T&&>::value << std::endl;  
    std::cout << value << std::endl;  
}

int main()
{
    std::string text = "hello";
    foo(text);
    foo(1);
}

您可以通过引用或值传递参数,右值规则将使用适当的类型。

为了避免代码重复, 可以扩展为从重载中实际调用模板:

#include <string>
#include <iostream>
#include <type_traits>
#include <boost/core/demangle.hpp>

template<typename T>
void foo( T value )
{
    std::cout << "inside template" << std::endl;
    std::cout << boost::core::demangle(typeid(value).name()) << std::endl;
}

void foo(const std::string &value)
{
    std::cout << "inside const string overload" << std::endl;
    foo<const std::string&>(value);
}
int main()
{
    foo(10);
    foo(std::string("hello"));
    return 0;
}

输出

inside template
int
inside const string overload
inside template
std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >

live example