当前位置: 动力学知识库 > 问答 > 编程问答 >

c++ - Overload for std::string not detected in template code

问题描述:

I'm writing some template code to determine if a given type can be passed as any argument to any available overload of a function. In the example below I've used the log function, but I've also tried this code on others in the math library, and the results are the same. The idea is to use function overloading and the sizeof operator to distinguish between cases where the type in question can legally be passed to the function in question (log, in this example).

If it worked, we'd have sizeof(overload<type>(NULL)) == sizeof(True) when 'type' can be legally passed to log, and sizeof(overload<type>(NULL)) == sizeof(False) otherwise. This does seems to work for most types, but fails for std::string.

Here's exactly how it fails:

Under normal circumstances we have sizeof(overload<std::string>(NULL)) == sizeof(False), as we should. But, when I declare an overload of log that does take a string, it still doesn't trigger the sizeof(True) branch of the logic. Note that I don't actually want to declare log(std::string) function, I'm just testing this code to make sure that it's able to detect all possible overloads.

At first I thought it just wasn't detecting overloads properly, but when I tried it with a user-defined class ('MyClass' in the example below), it worked fine: it produced sizeof(True) when log(MyClass) was declared, and sizeof(False) otherwise.

#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;

}

网友答案:

Here's the same issue w/o the SFINAE distraction:

#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>();
}

Output:

void bar(...)
void bar(...)

Lookup of a dependent function name will be performed:

  • as (pure) unqualified lookup from the point of definition
  • as (pure) argument-dependent lookup from the point of instantiation

Therefore, the following example differs:

#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>();
}

Output:

void bar(...)
void bar(ns::string)

For std::string, the only associated namespace is std. The global namespace is not associated and will not be searched in the OP's code. Therefore, the overload declared after the template definition will not be found.

N.B. Please do not inject overloads into namespace std. This will lead to undefined behaviour as per [namespace.std]/1.

分享给朋友:
您可能感兴趣的文章:
随机阅读: