2

I am trying to understand the name lookup and argument dependency lookup.I have created a small example.

Edited:

https://godbolt.org/g/rMWUbe

#include <iostream>

void g(const int*) {}

template <typename T>
struct TypeResolution;

template <typename T>
struct TypeResolution<T&> {
    typedef const T* type;
    static constexpr void (*func_ptr)(TypeResolution::type) = g;
    static constexpr void *func_ptr_void = (void*)func_ptr;
};

template <typename T>
struct TypeResolution {
    typedef const T* type;
    static constexpr void (*func_ptr)(TypeResolution::type) = g;
    static constexpr void *func_ptr_void = (void*)func_ptr;
};

void foo_impl(void *[], void *[]) {
   //Some work here, that will be in a different file or library
}

template <typename... ARGS>
void foo(ARGS && ... args) {
   void *func_ptrs[] = { TypeResolution<ARGS>::func_ptr_void... };
   void *args_ptrs[] = {(void*)&args...};
   foo_impl(func_ptrs, args_ptrs);
}

struct MyClass {};
void g(const MyClass*) {}

int main(int argc, char* argv[]) {
   int i = 1;
   foo(i);

   int j = 2;
   foo(i, j);

   MyClass c;
   foo(c); //This fails.
}

So my question is, why it doesn't compile? Or more simply why the lookup of g inside the TypeResolution happens when the class is declared, and not when it is instantiated? As I expected inside the main function and then to see the function void g(const MyClass*)

What I want to obtain is to be able to call different functions for different different types, but without needing to forward declare them.

I am using g++ 5.4.0 on Ubuntu 16.04

Horatiu Vultur
  • 265
  • 2
  • 6
  • You mean to say `&g` doesn't compile right? There's no `g`. – Barry Dec 13 '17 at 23:46
  • Your code compiles for me with vs 2017 (15.5.1), VC 19.12.something, even with no c++ standard set. – SoronelHaetir Dec 14 '17 at 00:19
  • `g_call` depends on `T` - but I'm not sure how this is relevant; your problem is not with lookup of `g_call`, it's with lookup of `g`. When it appears in a function call, it's looked up twice - ordinary lookup at the point of definition, ADL at the point of instantiation (it's the latter that finds it). When it appears in `&g`, it's not a dependent name, and is only looked up once, at the point of definition. – Igor Tandetnik Dec 14 '17 at 00:32
  • 1
    @SoronelHaetir It works because MSVC notoriously doesn't implement two-phase lookup, and delays all lookup to the point of instantiation. It happily compiles `template void f() { random garbage here; }` as long as `f` is not actually used ([Demo](http://rextester.com/GPCV88315)) – Igor Tandetnik Dec 14 '17 at 00:34
  • 2
    You are asking for a workaround, but you are not describing what you want. You are simply showing code that does not compile. I could make your compile a countably infiinite number of ways, but with no clues asto what the underlying problem you are trying to solve I would not know what subset would be useful to you (if any), and SO has a post length limit that is finite. – Yakk - Adam Nevraumont Dec 14 '17 at 01:50

2 Answers2

2
template <typename T>
void foo(T t) {
   void(*f)(T) = +[](T x){ return g(std::forward<T>(x)); };
   f(t); //This compiles
   g(t); //This compiles
}

this may be what you want. Note that the argument is moved once within f, which should not matter for primitive types.

template <class T>
struct TypeResolution;

template <class T>
struct TypeResolution<T&>:TypeResolution<T> {};

template <class T>
struct TypeResolution {
  typedef const T* type;
  static constexpr void (*func_ptr)(type) = +[](type x){ return g(x); };
  static constexpr void *func_ptr_void = (void*)func_ptr;
};

requires because you cannot use lambdas in or in a constexpr expression.

In previous versions of C++ we can do:

template <class T>
struct TypeResolution {
  typedef const T* type;
  static void invoke_g(type t) { g( t ); }
  static constexpr void (*func_ptr)(type) = invoke_g;
  static constexpr void *func_ptr_void = (void*)func_ptr;
};
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

Based on Yakk's answer and based on n4487 I managed to implement a solution that I managed to compile with c++14.

https://godbolt.org/g/BRee4u

#include <iostream>
#include <type_traits>
#include <string>

void g(int*) {}

template <typename T>
struct TypeResolution;

template <typename T>
struct TypeResolution<T&> {
    struct Inner {
        static constexpr void foo(T* t) { g(t); }
    };
    static constexpr void *func_ptr_void = (void*)Inner::foo;
};

template <typename T>
struct TypeResolution {
    struct Inner {
        static constexpr void foo(T* t) { g(t); }
    };
    static constexpr void *func_ptr_void = (void*)Inner::foo;
};

void foo_impl(void *func_args[], void *data_args[]) {
    //Some work here, that will be in a different file or library
}

template <typename... ARGS>
void foo(ARGS && ... args) {
    void *func_ptrs[] = { TypeResolution<ARGS>::func_ptr_void... };
    void *args_ptrs[] = {(void*)&args...};
    foo_impl(func_ptrs, args_ptrs);
}

struct MyClass {};
void g(const MyClass*) {}

int main(int argc, char* argv[]) {
    int i = 1;
    foo(i);

    MyClass c;
    foo(c);
}
Horatiu Vultur
  • 265
  • 2
  • 6