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.
Related
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.
The code below, compiled with gcc 4.9 with -std=c++14 -Wshadow produces the following warning:
main.cpp:12:24: warning: declaration of ‘id’ shadows a member of 'this' [-Wshadow]
main.cpp:
#include <string>
class Foo
{
public:
Foo(std::string id);
const std::string& id() const noexcept;
private:
const std::string m_id;
};
Foo::Foo(std::string id) : m_id(id) {}
const std::string& Foo::id() const noexcept { return m_id; }
int main(int argc, char** argv) { return 0; }
The codebase I inherited contains a lot of such code. The naming convention the original authors chose (and I have to stick to) requires that getters are not prefixed with "get". Renaming parameters such as id just for the sake of getting rid of this warning sounds like a lot of work for nothing. At the moment I'm leaning towards adding a trailing underscore - it only has to be done in .cpp files so that ugliness won't reach public headers. Does anybody have any other ideas?
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 have defined some static functions in their own .h and .cpp files, so these may be called from elsewhere without need for instantiation:
functions.h
#pragma once
class functions {
public:
static const int addition(const int&, const int&);
static const int product(const int&, const int&);
};
functions.cpp
#include "functions.h"
const int functions::addition(const int& op1, const int& op2) {
return op1 + op2;
}
const int functions::product(const int& op1, const int& op2) {
return op1 * op2;
}
I have also created a section for program settings where I will define which of the functions to pick:
constants.h
#pragma once
const int modulus = 10;
extern const int(*operation)(const int&, const int&);
constants.cpp
#include "constants.h"
#include "functions.h"
const int(*operation)(const int&, const int&) = &functions::addition;
This code works as expected using the following sample:
main.cpp
#include <iostream>
#include "constants.h"
int main() {
int a = 7, b = 4;
std::cout << operation(a,b) % modulus << std::endl;
}
Problem is, now I would like to parametrize the functions addition and product using a non-type template, such as:
functions.h
#pragma once
class functions {
public:
template<const int&> static const int addition(const int&, const int&);
};
functions.cpp
#include "functions.h"
template<const int& sub> const int functions::addition(const int& op1, const int& op2) {
return op1 + op2 - sub;
}
The later code breaks, no matter how I try to adapt the other files. I have done everything I reasonably (and pointlessly) could imagine.
Any help on how to rewrite the code on constants.* will be very appreciated.
Templates are instantiated at compile time, and for that the definition must be known - you need to have the template definition in the header.
Even if you do that, you don't have an int as the parameter but a const int&.
That means that you need to instantiate it with an lvalue whose identity (i.e. location) can be determined at compile-time.
In turn, that means that the parameter must be a variable with external linkage - not a temporary or a local variable.
In other words:
extern int x;
void foo()
{
addition<1>(2,3); // Not good; not an lvalue
const int y = 1;
addition<y>(2,3); // Not good; no linkage
addition<x>(2,3); // Good
}
You probably want to use template<int sub> instead.
(Another side note: const references to primitive types are pointless. All they do is add overhead.)
Template definitions should either be visible to the compiler during instantiation, or explicitly instantiated. Since I highly doubt you can explicitly instantiate all your possible versions of integer template, you have put the defginitons into the header.
Than comes the problem of operations. Since it's a function pointer, it can only point to specific instance of your template - the one instantiated with specific integer template. Doubt it's usable at all.
I suggest to rethink your design.
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());
}