I have a C-struct (in a C header file) that looks like so:
struct Foo {
int a;
int b;
int c;
};
typedef struct Foo Foo;
I want to test equality of two vectors of these structs, and so I would like to define a custom equality operator for this struct for just my translation unit.
I am able to do so with
static inline bool operator==(const Foo&, const Foo&) {...}
but not with
namespace {
bool operator==(const Foo&, const Foo&) {...}
}
Why can the equality template for std::vector not find this operator, and is there a better way than tossing a static inline in the global namespace?
I think you'll find that std::vector actually does find the operator in the anonymous namespace, if that operator's declaration occurs before #include <vector>.
The reason it doesn't find it in your code is related to the two-phase lookup for names in template functions. The first phase finds candidates in all namespaces in scope. The second phase only finds dependent names. "Argument-dependent lookup" which searches the namespace containing the type is dependent just as the phrase says. Lookup in the anonymous namespace is not dependent, so it won't be done during the second phase.
See also:
Declare function after template defined
Template specialization doesn't see a function in its point of instantiation
Why does the compiler find my function if is not yet declared?
The first thing to be noted is that an anonymous namespace is not the same as no namespace.
namespace
{
bool operator==(const Foo&, const Foo&) {...}
}
is really something like
namespace ANameUniqueToTheFile
{
bool operator==(const Foo&, const Foo&) {...}
}
using ANameUniqueToTheFile;
with the downside that the language does not give you the ability to get the name of the namespace.
For that reason, the operator== function defined in the anonymous namespace is not found using ADL.
I can see why you would want to put the operator== function in a namespace. One possbile way to do that would be to #include the .h file that defines the struct inside a named namespace.
FooWrapper.h:
namespace MyApp
{
#include "foo.h"
// Declare the function.
bool operator==(Foo const& lhs, Foo const& rhs);
}
FooWrapper.cpp
#include "FooWrapper.h"
namespace MyApp
{
// Implement the function.
bool operator==(Foo const& lhs, Foo const& rhs) { ... }
}
However...
I realize that I'm simplifying a bit about what's in "foo.h". Adding all of them in namesapce MyApp might not be appropriate, specially if "foo.h" includes other .h files and/or standard header files. I am hoping it gives you some ideas on how to tackle the problem.
If putting all of "foo.h" in namespace MyApp does not work smoothly, it will probably be expedient to define the operato== function in global scope.
Related
I've long since stopped using static for helper functions in favor of an anonymous namespace, which has the advantage of working with types, variables, and templates as well as for functions.
However, I was surprised when a function was not found when I replaced a call to it with a wrapper template. See code at https://godbolt.org/z/GrojceqGx with compiler and options that match my project.
#include <utility>
//#define WORKING
class C {};
template <typename Left, typename Right>
auto wrapper (Left&& left, Right&& right, const char* name)
{
return foo (std::forward<Left>(left),std::forward<Right>(right));
}
#ifdef WORKING
static
void foo (C& left, int right)
{
// compiles when using static function
}
#else
namespace {
void foo (C& left, int right)
{
// fails to compile in anonymous namespace
}
}
#endif
void sample()
{
C x;
wrapper (x, 17, "call 1");
}
Why does the instantiation of wrapper see foo when it is static but not when it is in an anonymous namespace? The point of instantiation is in this translation unit, in the same spot either way.
Because the anonymous namespace is, believe it or not, another namespace entirely. And not the global namespace.
foo is found by ADL when you use static. Because now foo is properly in the associated namespace of C (the global namespace).
It will work however for an inline anonymous namespace, i.e.
inline namespace { }
Since ADL is designed to work nice with inline namespaces.
i'm trying to implement a clone of the json serialization library nlohmann::json as a learning experience, and i'm having trouble with the interface for user defined (json<->User type) conversion.
Basically i want the user to be able to overload two function: to_json(json&, const Type&) and from_json(const json&, Type&). Then the library will use overload resolution to call theses function in the templated operator= and one argument constructor.
It works fine when i'm just defining theses function directly but when i try to make a template definition for multiple types (in this example the class S) the linker can't find the definition.
I've tried to explicitly instantiate the function for individual instances of the templated class although i would prefer avoiding having to do that in the final product.
I'm guessing it has to do with the fact that templated function don't have the same signature than free function, but i don't see what i can do to make it work. What am i missing ? I also couldn't find result on google so is it a documented pattern or an anti pattern ?
Thanks you. Below i tried to minimize my problem in one short example.
Class.hpp
#pragma once
#include <cstdio>
template<size_t i>
class S {
size_t n = i;
};
template<size_t i>
void g(const S<i>& s) {
printf("S<%u>\n", i);
}
Class.cpp
#include "Class.hpp"
template void g<10>(const S<10>&); // <-- Even with explicitly instanciation
void g(const bool& b) {
printf("%s\n", b ? "true" : "false");
}
main.cpp
#include "Class.hpp"
template<typename T>
void f(T t) {
extern void g(const T&);
g(t);
}
int main(int, char**) {
S<10> s;
//f(s); <-- linker error: void g(class S<10> const &) not found.
f(false);
}
The name lookup for g in g(t) call stops as soon as it finds extern void g(const T&); declaration; it never sees the declaration of the function template. So the compiler generates a call to a regular non-template function named g taking const S<10>&. But no such function is defined in your program - hence linker error.
I am writing a simple logger class similar to QDebug, which has a template method that save data into QStringList. The code is here:
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QTextStream>
class Logger
{
public:
Logger();
~Logger();
template <typename V>
Logger &operator<<(V const &value);
private:
QStringList msg;
};
inline Logger::Logger():
msg(QString("INFO:"))
{}
inline Logger::~Logger()
{
QTextStream out(stderr);
out << msg.join("");
}
template <typename V>
inline Logger &Logger::operator<<(V const &value)
{
msg << log(value);
return *this;
}
inline QString log(QString const &value)
{
return value;
}
inline QString log(int const (&value)[20])
{
return QString("Array");
}
int main(int argc, char *argv[])
{
Logger c;
int a[20] = {};
c << QString("test") << a;
return 0;
}
However, this doesn't compile with GCC 4.8.3.
$ g++ -I/usr/include/qt4 -L/usr/lib64/qt4 -lQtCore -o test2 test2.cpp
test2.cpp: In instantiation of ‘Logger& Logger::operator<<(const V&) [with V = int [20]]’:
test2.cpp:50:29: required from here
test2.cpp:32:21: error: ‘log’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
msg << log(value);
^
test2.cpp:41:16: note: ‘QString log(const int (&)[20])’ declared here, later in the translation unit
inline QString log(int const (&value)[20])
Indeed, if I move inline QString log(int const (&value)[20]) to the beginning or put a forward declaration, it compiles and works. But what confuses me is that inline QString log(QString const &value) works without any problem:
$ ./test2
INFO:testArray
I notice that QHash relies on qHash function, which is similar in this case. QHash works pretty fine with user-defined key classes (except arrays, which can't be a function return type).
Why do they behave differently? What did I miss here?
Thank you for your help.
BTW: Would you please tell me what would be good keywords for this question. I've tried combinations of "specialiation" "template" "forward declaration" "QHash" and "user-defined types", but they don't work.
The name log is looked up twice. At the point of template definition, ordinary lookup is performed. It doesn't find anything, since log is not declared at this point.
Then, at the point of instantiation, only argument-dependent lookup is performed. When the parameter is of type QString, the global namespace is searched since QString is declared there, and so log(QString) is found. But the type int[] doesn't have any associated namespaces, so argument-dependent lookup has nothing to search and finds nothing. Hence the error.
I am trying to get the following code to compile on clang, but it fails with the following error:
error: no member named 'operator<' in the global namespace
I tried compiling the visual studio code with /Za to switch to standard conformance, but it still seems to accept it. Enlighten me please.
struct A
{
int m_test;
A(int test)
: m_test(test)
{
}
friend bool operator<(A left, A right);
};
int main()
{
typedef bool(*TCompare)(A,A);
TCompare compare = &::operator<;
compare(9,7);
}
VC++ output: https://godbolt.org/g/LAz56n
Clang output: https://godbolt.org/g/zC2InO
Clang is correct. For your sample code, the name operator< introduced by friend declarations does become the member of the global namespace, but it's not visible to name lookup. It can only be found by ADL, but ADL only applies to function-call expressions, while &::operator< is not.
Names introduced by friend declarations within a non-local class X
become members of the innermost enclosing namespace of X, but they do
not become visible to lookup (neither unqualified nor qualified)
unless a matching declaration is provided at namespace scope, either
before or after the class definition. Such name may be found through
ADL which considers both namespaces and classes.
To fix it you have to add a matching declaration at global namespace scope. e.g.
struct A
{
int m_test;
A(int test)
: m_test(test)
{
}
friend bool operator<(A left, A right);
};
// add a matching declaration at namespace scope
bool operator<(A left, A right);
int main()
{
typedef bool(*TCompare)(A,A);
TCompare compare = &::operator<;
compare(9,7);
}
bool operator<(A left, A right) {
...
}
It's a VC++ bug. The code is invalid.
A friend declaration doesn't introduce the declared name to the global namespace. You still need to declare it outside of the class.
I am trying to do some overload on the template function, following are the examples
do_something.h
template<typename T>
void do_something(T const &input){/*....*/}
void do_something(std::string const &input);
void do_something(boost::container::string const &input);
so far, so good, but what if I want to overload a non-defined type?
like using a type some_type have not defined in the header file
void do_something(some_type const &input);
I want to use it like this
main.cpp
#include "do_something.h"
#include "some_type.h"
#include <boost/container/string.hpp>
int main()
{
do_something(std::string("whatever"));
do_something(boost::container::string("whatever"));
//oops, some_type() never defined in the header file, this
//function will call the template version, but this is not
//the behavior user expected
do_something(some_type());
}
Since some_type is not a POD, not a std::string, boost::container::string.I guess I could designed a traits to do some compile time checking
template<typename T>
typename boost::enable_if<is_some_type<T>::value, T>::type
do_something(T const &input){//.....}
But do I have a better way to do it?
I need compile time type checking, so I use template.All of the types calling this function will do similar jobs based on different types, so I prefer overload.I do not need to save the state, so I prefer function rather than class.
Hope this could help you know more about what I intent to do.Thank you
but what if I want to overload a non-defined type?
You need to provide the declaration of
void do_something(some_type const &input);
before you call do_something with an object of type some_type. Otherwise, the template version will be used.
#include "do_something.h"
#include "some_type.h"
// This is all you need. You can implement the function here
// or any other place of your choice.
void do_something(some_type const &input);
#include <boost/container/string.hpp>
int main()
{
do_something(std::string("whatever"));
do_something(boost::container::string("whatever"));
//oops, some_type() never defined in the header file, this
//function will call the template version, but this is not
//the behavior user expected
do_something(some_type());
}