【问题标题】:Overload for std::string not detected in template code在模板代码中未检测到 std::string 的重载
【发布时间】:2014-01-14 22:04:10
【问题描述】:

我正在编写一些模板代码来确定是否可以将给定类型作为任何参数传递给任何可用的函数重载。在下面的示例中,我使用了 log 函数,但我也在数学库中的其他人身上尝试了此代码,结果是相同的。这个想法是使用函数重载和 sizeof 运算符来区分有问题的类型可以合法地传递给有问题的函数的情况(在本例中为日志)。

如果它成功了,当 'type' 可以合法地传递给 log 时,我们将拥有 sizeof(overload<type>(NULL)) == sizeof(True),否则将拥有 sizeof(overload<type>(NULL)) == sizeof(False)。这似乎对大多数类型都有效,但对std::string 无效。

这正是它失败的原因:

在正常情况下,我们应该有sizeof(overload<std::string>(NULL)) == sizeof(False)。但是,当我声明一个使用字符串的日志重载时,它仍然不会触发逻辑的 sizeof(True) 分支。请注意,我实际上并不想声明 log(std::string) 函数,我只是在测试这段代码以确保它能够检测到所有可能的重载。

起初我认为它只是没有正确检测过载,但是当我使用用户定义的类(下例中的“MyClass”)尝试它时,它运行良好:它在log(MyClass) 时产生了sizeof(True)被声明,否则 sizeof(False)。

#include <iostream>
#include <math.h>

template<int>
struct TakesInt{};


struct True
{
};

struct False
{
  // guarantees that sizeof(False) != sizeof(True)
  True array[2];
};


// takes anything; fall back if no match can be found
template<typename T>
False overload(...);

// takes a specific type; does not actually call log
template<typename T>
True overload(TakesInt<sizeof(log(T()))>*);

// As a test, this is an overload of log that takes a string.
// I don't actually want to implement this, but it should make the compiler
// think that a string is a valid argument.
double log(std::string);


// a placeholder for user defined class; could really be anything,
// like an arbitrary number class
struct MyClass{};

// declaring log for the arbitrary class above
// note that this is the same as for the log(std::string)
// if one works, the other should
double log(MyClass);

int main()
{
  std::cout << sizeof(True) << '\t' << sizeof(False) << std::endl;
  std::cout << sizeof(overload<std::string>(NULL)) << std::endl;
  std::cout << sizeof(overload<double>(NULL)) << std::endl;
  std::cout << sizeof(overload<MyClass >(NULL)) << std::endl;
  return 0;
}

【问题讨论】:

  • 听起来像是名称查找问题。 double log(std::string); 不是从True overload(TakesInt&lt;sizeof(log(T()))&gt;*); 的定义点通过非限定查找找到的,但是您自己的类型MyClass 从实例化点通过ADL 找到。我刚刚验证了当您将log(std::string) 的重载放在overload 之前时,它将通过不合格的查找找到:Live example

标签: c++ string function templates overloading


【解决方案1】:

这是没有 SFINAE 分心的相同问题:

#include <iostream>

namespace ns
{
    struct string {};
}

void bar(...) { std::cout << "void bar(...)\n"; }

template<class T>
void foo()
{
    T x{};
    bar(x);
}

void bar(ns::string) {  std::cout << "void bar(ns::string)\n";  }

int main()
{
    foo<int>();
    foo<ns::string>();
}

输出:

空栏(...) 空栏(...)

将执行依赖函数名的查找:

  • 作为(纯)非限定查找从定义的角度来看
  • 从实例化点作为(纯)依赖于参数的查找

因此,下面的例子有所不同:

#include <iostream>

namespace ns
{
    struct string {};
}

void bar(...) { std::cout << "void bar(...)\n"; }

template<class T>
void foo()
{
    T x{};
    bar(x);
}

namespace ns
{
    void bar(ns::string) {  std::cout << "void bar(ns::string)\n";  }
}

int main()
{
    foo<int>();
    foo<ns::string>();
}

输出:

空栏(...) 空栏(ns::string)

对于std::string,唯一关联的命名空间是std。全局命名空间关联,不会在 OP 的代码中搜索。因此,将找不到模板定义之后声明的重载。

注意请不要将重载注入命名空间std。根据 [namespace.std]/1,这将导致未定义的行为。

【讨论】:

    猜你喜欢
    • 2018-05-11
    • 2017-01-13
    • 1970-01-01
    • 2017-06-16
    • 2012-10-25
    • 2019-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多