标签调度,可变参数模板,通用引用和错过的const说明符

sky*_*ack 4 c++ const variadic-templates c++11

请考虑以下示例(标签调度,可变参数模板,完美转发等等):

#include <iostream>
#include <utility>
#include <string>

struct A { };
struct B { };

void doIt(A&&, const std::string &) {
    std::cout << "A-spec" << std::endl;
}

template<typename T, typename... Args>
void doIt(T&&, Args&&...) {
    std::cout << "template" << std::endl;
}

template<typename T, typename... Args>
void fn(Args&&... args) {
    doIt(T{}, std::forward<Args>(args)...);
}

int main() {
    const std::string foo = "foo";
    std::string bar = "bar";
    fn<A>(foo);
    fn<A>(bar);
    fn<B>(foo);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,输出是:

A-spec
template
template
Run Code Online (Sandbox Code Playgroud)

原因很明显,它并没有打扰我.

我想要实现的是doIt在两种情况下调用函数的第一个实例,无论字符串是否具有const说明符.
当然,一个可能的解决方案是doIt使用正确的原型定义一个新的,无论如何我想知道是否还有另一个解决方案.

到目前为止,我已经试图通过它来获得它add_const,但我很确定我错过了一些东西.
有没有可行的解决方案来静默添加const说明符并让它工作?

编辑

我已经更新了上面的例子,以便更加符合真正的问题.
此外,尽管有趣的答案,我忘了引用这只是一个简化的例子,所以真正的问题不仅仅涉及std::string.相反,它可能发生(作为示例)标记A参数是intconst std::string &,而对于标记B,参数是float,类的实例C,等等.
因此,试图以某种方式解决问题的那些答案std::string将无法解决真正的问题,对不起.

Pio*_*cki 5

引入两个独立的函数,以便它们不会相互冲突,编译器不会引起任何歧义错误:

void doIt(A&&, const std::string &)
{
    std::cout << "A-spec" << std::endl;
}

template <typename T, typename... Args>
void doIt_template(T&&, Args&&...)
{
    std::cout << "template" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

优先考虑两个额外的重载; 首选的是尝试调用目标函数的专用非模板化版本:

template <typename T, typename... Args>
auto fn_impl(int, Args&&... args)
    -> decltype(doIt(T{}, std::forward<Args>(args)...), void())
{
    doIt(T{}, std::forward<Args>(args)...);
}

template <typename T, typename... Args>
void fn_impl(char, Args&&... args)
{
    doIt_template(T{}, std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

介绍一个单一的通用调度员:

template <typename T, typename... Args>
void fn(Args&&... args)
{
    fn_impl<T>(0, std::forward<Args>(args)...);
}
Run Code Online (Sandbox Code Playgroud)

DEMO