c++ - Order of function definition during template instantiation -


i have trouble understanding order of template instantiation. seems compiler not consider function if defined "too late." following steps illustrates main ideas of code below:

  1. the framework should provide free function convert<from, to> if can find working overload function generate.

  2. the function to<t> shortcut convert<from,to> , should work if convert<from,to> valid.

  3. users should able provide overload of generate , able use to , convert.

the corresponding code:

#include <string> #include <utility> #include <iostream>  // if move code down below @ [*] location, works // expected.  // ------------- framework code -------------  // can generated can converted string. template <typename from> auto convert(from const& from, std::string& to)   -> decltype(       generate(std::declval<std::back_insert_iterator<std::string>&>(), from)       ) {   to.clear();   auto = std::back_inserter(to);   return generate(i, from); }  // similar convert, except directly returns requested type. template <typename to, typename from> auto to(from const& f) -> decltype(convert(f, std::declval<to&>()), to()) {   t;   if (! convert(f, t))     throw std::invalid_argument("invalid conversion");   return t; }  // ------------- user code -------------  // [*] support arithmetic types. template <typename iterator, typename t> auto generate(iterator& out, t i)   -> typename std::enable_if<std::is_arithmetic<t>::value, bool>::type {   // note: merely use std::to_string illustration purposes here.   auto str = std::to_string(i);   out = std::copy(str.begin(), str.end(), out);   return true; }  int main() {   uint16_t s = 16;   std::cout << to<std::string>(s) << std::endl;   return 0; } 

the problem in following code works if function generate appears before definition of convert , to. how can work around problem?

maybe mental model wrong here, thought template when compiler sees to<std::string>(uint16_t), starts going backwards , instantiate needed. guidance appreciated.

the compiler not know of existence of generate time sees definition of convert , to, have guessed yourself. contrary thought, doest not though sort of put definitions of convert , to "on hold" until sees generate is. workaround problem need forward declare generate, can done using following construction:

template <typename iterator, typename t> auto generate(iterator& out, t i)   -> typename std::enable_if<std::is_arithmetic<t>::value, bool>::type; 

this should appear right before definition of convert, compiler knows generate exists , function time compiles convert , to. way compiler can check syntax , guarantee valid call generate, before knows generate does, since needs @ point check if types of arguments of return value match, according rules defined language standard.

by doing this, naturally enforce specific type signature generate (remember compiler required check types when compiles convert , to!). if don't want that, , don't, best approach expect further template argument convert , to likewise, expect callable, is, can use in function call:

template <typename from, typename generator> auto convert(from const& from, std::string& to, generator generate)   -> decltype(       generate(std::declval<std::back_insert_iterator<std::string>&>(), from)       ) {   to.clear();   auto = std::back_inserter(to);   return generate(i, from); } 

these kind of objects commonly known callable objects.

the drawback approach because c++ unfortunately not support concepts yet, can't enforce requirements callable object generate should attend to. nonetheless, approach std library uses algorithms.

the advantage of approach can flexible, any possible callable object, minimally attends type requirements, can used. includes free functions, function objects, member functions through binding, among others. not mention user absolutely free choose name wants callable object instead of being forced use generate, initial idea require if valid c++.

now call modified version of convert using free function generateyou defined, that:

to<std::string>(s, generate<std::back_insert_iterator<std::string>, uint16_t>); 

that isn't nice, because since must explicitly state template arguments, approach fails take full advantage of fact generate template function. fortunately inconvenience can overcome use of function objects, following instance:

struct generator {     template <typename iterator, typename t>     auto operator()(iterator& out, t i)       -> typename std::enable_if<std::is_arithmetic<t>::value, bool>::type     {       // note: merely use std::to_string illustration purposes here.       auto str = std::to_string(i);       out = std::copy(str.begin(), str.end(), out);       return true;     } }; 

the previous call become simply

to<std::string>(s, generator()); 

taking full advantage of tamplate nature.

at rate, if got idea correctly, part of code of responsability of user, she, deserves, have full autonomy decide way prefers.


Comments

Popular posts from this blog

html - How to style widget with post count different than without post count -

How to remove text and logo OR add Overflow on Android ActionBar using AppCompat on API 8? -

javascript - storing input from prompt in array and displaying the array -