3

So I'm trying to understand what's happening with Boost's ptree implementation.

In ptree.hpp basic_ptree is actually defined:

template<class Key, class Data, class KeyCompare>
class basic_ptree

In ptree_fwd.hpp there is what looks like a forward declaration of basic_ptree but with a new template argument default:

template < class Key, class Data, class KeyCompare = std::less<Key> >
class basic_ptree;

And finally in ptree_fwd.hpp ptree is typedef'd:

typedef basic_ptree<std::string, std::string> ptree;

This is a forward declaration then in ptree_fwd.hpp, right? So I am allowed to default a template argument in a forward declaration?

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 1
    You can declare a default value for a template parameter in a template declaration just like you can declare a default value for a function parameter in a function declaration. – Sam Varshavchik Jan 16 '18 at 13:55
  • @SamVarshavchik Yup, knew that. Can I default a template parameter in a forward declaration? – Jonathan Mee Jan 16 '18 at 13:58
  • 2
    That's what I wrote. – Sam Varshavchik Jan 16 '18 at 14:01
  • @JonathanMee You can do it for both declarations (might be multiple times) and definition, and they will be merged at last; same as function arguments. – songyuanyao Jan 16 '18 at 14:18
  • @songyuanyao It sounds like you're saying that I *can* default an argument in a forward declaration... Which I guess makes my own forward declaration more confusing. I'd really like intellisense to jump to ptree.hpp's `basic_ptree` definition when I click on "Go To Definition" of my local `ptree`s, but I guess this explains why it must jump to my forward definition instead. – Jonathan Mee Jan 16 '18 at 14:41
  • @songyuanyao They are merged, but you may specify a default for each parameter only once. Since we're talking about a single default here, he can't specify it in both declarations. – oisyn Jan 16 '18 at 15:14
  • @JonathanMee It's wise to put the default arguments in the forward declaration, so they are available when using the template in contexts where the definition is not yet known. And then make sure that that forward declaration is also known at time of definition. – oisyn Jan 16 '18 at 15:16
  • @oisyn I feel like this gets really nasty really quick. What if I include 2 separate headers that include different defaults in their forward declarations? Or is that what you're addressing? – Jonathan Mee Jan 16 '18 at 15:31
  • 1
    @JonathanMee I'm writing up an answer, it'll be clear in a moment :) – oisyn Jan 16 '18 at 15:32

1 Answers1

3

Yes, you can. But you can specify each default template argument only once.

17.1/14

The set of default template-arguments available for use is obtained by merging the default arguments from all prior declarations of the template in the same way default function arguments are (dcl.fct.default).

[ Example:

template<class T1, class T2 = int> class A;
template<class T1 = int, class T2> class A;

is equivalent to

template<class T1 = int, class T2 = int> class A;

— end example ]

And 17.1/16

A template-parameter shall not be given default arguments by two different declarations in the same scope. [ Example:

template<class T = int> class X;
template<class T = int> class X { /* ... */ };  // error

— end example ]

( Note, these are taken from the latest draft, but these rules haven't changed in recent years to my knowledge )

If you want to be able to make use of the default arguments when only the declaration is know, you'd have to define them in the declaration. Above also answers the question that you asked in the comments:

I feel like this gets really nasty really quick. What if I include 2 separate headers that include different defaults in their forward declarations? Or is that what you're addressing?

If you did that your program would be ill-formed, as you are allowed to specify each default argument only once. Having two declarations, where one defines the default arguments and the other doesn't, does not really cause any problems because they are not in disagreement of eachother. It only means that if only the version without defaults is known, you'd have to specify all arguments when instantiating your template.

My personal opinion would be to have only one declaration in a header that specifies the default template arguments, and then include that header wherever you need the declaration (and for the definition as well).

oisyn
  • 1,306
  • 5
  • 14
  • So as far as my own forward declarations, I clearly can no longer forward declare a default. But then it wouldn't match :( Do I have to do that nasty thing in the `typedef` where I'm defaulting there, cause then in reality my code's `ptree` usage is defaulting to my `typedef` rather than using that defined in ptree_fwd.hpp, which obviously has very negative ramifications if Boost was to change anything. – Jonathan Mee Jan 16 '18 at 16:25
  • So my question was originally really 2 questions rolled into 1. I've simplified this question making this a more complete answer. But I've also forked the other half of the question into a separate question. If you felt so inclined I'd appreciate some direction here as well: https://stackoverflow.com/q/48302501/2642059 – Jonathan Mee Jan 17 '18 at 13:45