From: daniel.kruegler@googlemail.com   
      
   Am 22.01.2012 09:50, schrieb Jonathan Thornburg:   
   > I recently ran across an interesting problem in combining two parts of   
   > C++ that individually work beautifully: boost::format and in-class   
   > 'static const int' constants.   
   >   
   > The following toy code illustrates the problem:   
   >   
   > #include   
   > #include "boost/format.hpp"   
   >   
   > using std::cout;   
   > using boost::format;   
   >   
   > // identity function   
   > inline int I(int x) { return x; }   
   >   
   > // declaration of class containing in-class 'static const int'   
   > class foo   
   > {   
   > public:   
   > static const int N = 42;   
   > };   
   >   
   > int main()   
   > {   
   > cout<< "raw output: N = "<< foo::N<< "\n";   
   > cout<< "raw output: I(N) = "<< I(foo::N)<< "\n";   
   > #if 0   
   > cout<< format("boost::format output: N = %d\n") % foo::N;   
   > #endif   
   > cout<< format("boost::format output: I(N) = %d\n") % I(foo::N);   
   >   
   > return 0;   
   > }   
   >   
   > Here we've declared a class foo containing an in-class constant int   
   > foo::N . So long as we don't try to take the address of foo::N   
   > (i.e., so long as we don't try to form a pointer or reference to it)   
   > everything is fine, and in particular we don't need to explicitly   
   > define foo::N outside the class declaration.   
      
   Correct. More precisely, the criterion for a required definition (as of   
   C++11) is whether the object is odr-used.   
      
   > In particular, there's no problem in printing foo::N by sending it to   
   > std::cout , or in passing foo::N by value to a function I() . And   
   > there's no problem in using boost::format to print I(foo::N) .   
   > However, if we change the "#if 0" to "#if 1", and thus try to print   
   > foo::N using boost::format , then the program fails to link   
   [..]   
   > because boost::format takes its arguments by reference-to-const, so   
   > we're now asking the compiler to form a reference to foo::N , which   
   > has never been explicity defined.   
      
   Right.   
      
   [..]   
   > So, what to do if we want to use boost::format to print out (messages   
   > which include) in-class constant ints? I can see several possible   
   > solutions:   
   >   
   > We could explicitly define the in-class constant ints outside the class   
   > declaration. This is awkward on two grounds:   
   > * it makes the code less readable: N must then be initialized in the   
   > definition,   
      
   This is wrong, providing the definition separately does not require to   
   move the initializer to the point of the definition. According to   
   [class.static.data] p3:   
      
   "[..] The member shall still be defined in a namespace scope if it is   
   odr-used (3.2) in the program and the namespace scope definition shall   
   not contain an initializer."   
      
   > which lives in a .cc file far away (in the source code)   
   > from the .hh file where the class is declared, so reading the .hh file   
   > no longer tells the programmer a key piece of information about N   
   > (its value).   
   > * if the class is actually a template, the definition needs to be   
   > replicated for each instantiation of the template   
      
   I don't know what you mean with "replicated", and I don't see a problem   
   here for you. It is the compiler who has to ignore the multiple   
   instantiations, not you.   
      
   > We could use an enum instead of 'static const int', so as to get a truly   
   > compile-time constant. Alas, boost::format (at least in the version I'm   
   > using) doesn't know how to output an enum, so code trying to do so fails   
   > to compile:   
   >> boost-format-call-by-ref2.cc:26: error: no match for 'operator%' in   
   'boost::basic_format, std::allocator    
   >(((const char*)"boost::format output: M = %d\012")) % M'   
   >   
   > We could use an explicit identity function I() which takes its argument   
   > by *value*, and (as in the above code) apply I() to the in-class constant   
   > int before printing.   
   >   
   > At the moment my preferred solution is the last of these, with an explicit   
   > identity function. Are there other (more-elegant) solutions? Are there   
   > other cogent arguments for/against various solutions?   
      
   I don't see any reason for enums or explicit identity functions, just   
   provide a definition without initializer and you are done. The single   
   ugliness of this solution is that you have to provide the definition in   
   a separate file.   
      
   HTH & Greetings from Bremen,   
      
   Daniel Krügler   
      
      
   --   
    [ See http://www.gotw.ca/resources/clcm.htm for info about ]   
    [ comp.lang.c++.moderated. First time posters: Do this! ]   
      
   --- SoupGate-Win32 v1.05   
    * Origin: you cannot sedate... all the things you hate (1:229/2)   
|