Pimpl - Why can make_unique be called on an incomplete type

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
25
down vote

favorite
7












Why does the make_unique call compile? Doesn't make_unqiue require its template argument to be a complete type ?



struct F;
int main()

std::make_unique<F>();


struct F ;



The question orignated from my "problem" with my PIMPL implementation:



I do understand why the destructor has to be user declared and defined inside the cpp file for the Implementation class (PIMPL).



But moving the constructor of the class containing the pimpl- still compiles.



class Object
;

class CachedObjectFactory

public:
CachedObjectFactory();

~CachedObjectFactory();
std::shared_ptr<Object> create(int id) const;

private:
struct CacheImpl;
std::unique_ptr<CacheImpl> pImpl;
;


Now the cpp file:



// constructor with make_unique on incompete type ?
CachedObjectFactory::CachedObjectFactory()
: pImpl(std::make_unique<CacheImpl>())


struct CachedObjectFactory::CacheImpl

std::map<int, std::shared_ptr<Object>> idToObjects;
;

//deferred destructor
CachedObjectFactory::~CachedObjectFactory() = default;


Could someone explain why this compiles?
Why is there a difference between construction and destruction?
If the instantiation of the destructor and the instantiation of the default_deleter is a problem why is the instantiation of make_unique not a problem ?










share|improve this question























  • You really don't include the header that defines CacheImpl in the .cpp file? I doubt that this compiles, for creation as well as desctrution, the type of CacheImpl must be known.
    – lubgr
    Sep 5 at 8:47










  • @lubgr - Believe it. It even builds if we put the nested struct last after everything coliru.stacked-crooked.com/a/e5f31d5306834b25
    – StoryTeller
    Sep 5 at 8:53










  • sorry i kept the example short of course did include the defintion of my cachedobjectfactory
    – AF_cpp
    Sep 5 at 8:59







  • 3




    @lubgr Here's a smaller example that also compiles: struct F; int main() std::make_unique<F>(); struct F;
    – molbdnilo
    Sep 5 at 8:59







  • 1




    @AF_cpp, you could remove all the first part of your question and only keep the mcve (starting with Or even simpler). It would improve the question.
    – YSC
    Sep 5 at 9:49














up vote
25
down vote

favorite
7












Why does the make_unique call compile? Doesn't make_unqiue require its template argument to be a complete type ?



struct F;
int main()

std::make_unique<F>();


struct F ;



The question orignated from my "problem" with my PIMPL implementation:



I do understand why the destructor has to be user declared and defined inside the cpp file for the Implementation class (PIMPL).



But moving the constructor of the class containing the pimpl- still compiles.



class Object
;

class CachedObjectFactory

public:
CachedObjectFactory();

~CachedObjectFactory();
std::shared_ptr<Object> create(int id) const;

private:
struct CacheImpl;
std::unique_ptr<CacheImpl> pImpl;
;


Now the cpp file:



// constructor with make_unique on incompete type ?
CachedObjectFactory::CachedObjectFactory()
: pImpl(std::make_unique<CacheImpl>())


struct CachedObjectFactory::CacheImpl

std::map<int, std::shared_ptr<Object>> idToObjects;
;

//deferred destructor
CachedObjectFactory::~CachedObjectFactory() = default;


Could someone explain why this compiles?
Why is there a difference between construction and destruction?
If the instantiation of the destructor and the instantiation of the default_deleter is a problem why is the instantiation of make_unique not a problem ?










share|improve this question























  • You really don't include the header that defines CacheImpl in the .cpp file? I doubt that this compiles, for creation as well as desctrution, the type of CacheImpl must be known.
    – lubgr
    Sep 5 at 8:47










  • @lubgr - Believe it. It even builds if we put the nested struct last after everything coliru.stacked-crooked.com/a/e5f31d5306834b25
    – StoryTeller
    Sep 5 at 8:53










  • sorry i kept the example short of course did include the defintion of my cachedobjectfactory
    – AF_cpp
    Sep 5 at 8:59







  • 3




    @lubgr Here's a smaller example that also compiles: struct F; int main() std::make_unique<F>(); struct F;
    – molbdnilo
    Sep 5 at 8:59







  • 1




    @AF_cpp, you could remove all the first part of your question and only keep the mcve (starting with Or even simpler). It would improve the question.
    – YSC
    Sep 5 at 9:49












up vote
25
down vote

favorite
7









up vote
25
down vote

favorite
7






7





Why does the make_unique call compile? Doesn't make_unqiue require its template argument to be a complete type ?



struct F;
int main()

std::make_unique<F>();


struct F ;



The question orignated from my "problem" with my PIMPL implementation:



I do understand why the destructor has to be user declared and defined inside the cpp file for the Implementation class (PIMPL).



But moving the constructor of the class containing the pimpl- still compiles.



class Object
;

class CachedObjectFactory

public:
CachedObjectFactory();

~CachedObjectFactory();
std::shared_ptr<Object> create(int id) const;

private:
struct CacheImpl;
std::unique_ptr<CacheImpl> pImpl;
;


Now the cpp file:



// constructor with make_unique on incompete type ?
CachedObjectFactory::CachedObjectFactory()
: pImpl(std::make_unique<CacheImpl>())


struct CachedObjectFactory::CacheImpl

std::map<int, std::shared_ptr<Object>> idToObjects;
;

//deferred destructor
CachedObjectFactory::~CachedObjectFactory() = default;


Could someone explain why this compiles?
Why is there a difference between construction and destruction?
If the instantiation of the destructor and the instantiation of the default_deleter is a problem why is the instantiation of make_unique not a problem ?










share|improve this question















Why does the make_unique call compile? Doesn't make_unqiue require its template argument to be a complete type ?



struct F;
int main()

std::make_unique<F>();


struct F ;



The question orignated from my "problem" with my PIMPL implementation:



I do understand why the destructor has to be user declared and defined inside the cpp file for the Implementation class (PIMPL).



But moving the constructor of the class containing the pimpl- still compiles.



class Object
;

class CachedObjectFactory

public:
CachedObjectFactory();

~CachedObjectFactory();
std::shared_ptr<Object> create(int id) const;

private:
struct CacheImpl;
std::unique_ptr<CacheImpl> pImpl;
;


Now the cpp file:



// constructor with make_unique on incompete type ?
CachedObjectFactory::CachedObjectFactory()
: pImpl(std::make_unique<CacheImpl>())


struct CachedObjectFactory::CacheImpl

std::map<int, std::shared_ptr<Object>> idToObjects;
;

//deferred destructor
CachedObjectFactory::~CachedObjectFactory() = default;


Could someone explain why this compiles?
Why is there a difference between construction and destruction?
If the instantiation of the destructor and the instantiation of the default_deleter is a problem why is the instantiation of make_unique not a problem ?







c++ templates language-lawyer unique-ptr pimpl-idiom






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Sep 5 at 11:35









SkepticalEmpiricist

4,628924




4,628924










asked Sep 5 at 8:38









AF_cpp

188111




188111











  • You really don't include the header that defines CacheImpl in the .cpp file? I doubt that this compiles, for creation as well as desctrution, the type of CacheImpl must be known.
    – lubgr
    Sep 5 at 8:47










  • @lubgr - Believe it. It even builds if we put the nested struct last after everything coliru.stacked-crooked.com/a/e5f31d5306834b25
    – StoryTeller
    Sep 5 at 8:53










  • sorry i kept the example short of course did include the defintion of my cachedobjectfactory
    – AF_cpp
    Sep 5 at 8:59







  • 3




    @lubgr Here's a smaller example that also compiles: struct F; int main() std::make_unique<F>(); struct F;
    – molbdnilo
    Sep 5 at 8:59







  • 1




    @AF_cpp, you could remove all the first part of your question and only keep the mcve (starting with Or even simpler). It would improve the question.
    – YSC
    Sep 5 at 9:49
















  • You really don't include the header that defines CacheImpl in the .cpp file? I doubt that this compiles, for creation as well as desctrution, the type of CacheImpl must be known.
    – lubgr
    Sep 5 at 8:47










  • @lubgr - Believe it. It even builds if we put the nested struct last after everything coliru.stacked-crooked.com/a/e5f31d5306834b25
    – StoryTeller
    Sep 5 at 8:53










  • sorry i kept the example short of course did include the defintion of my cachedobjectfactory
    – AF_cpp
    Sep 5 at 8:59







  • 3




    @lubgr Here's a smaller example that also compiles: struct F; int main() std::make_unique<F>(); struct F;
    – molbdnilo
    Sep 5 at 8:59







  • 1




    @AF_cpp, you could remove all the first part of your question and only keep the mcve (starting with Or even simpler). It would improve the question.
    – YSC
    Sep 5 at 9:49















You really don't include the header that defines CacheImpl in the .cpp file? I doubt that this compiles, for creation as well as desctrution, the type of CacheImpl must be known.
– lubgr
Sep 5 at 8:47




You really don't include the header that defines CacheImpl in the .cpp file? I doubt that this compiles, for creation as well as desctrution, the type of CacheImpl must be known.
– lubgr
Sep 5 at 8:47












@lubgr - Believe it. It even builds if we put the nested struct last after everything coliru.stacked-crooked.com/a/e5f31d5306834b25
– StoryTeller
Sep 5 at 8:53




@lubgr - Believe it. It even builds if we put the nested struct last after everything coliru.stacked-crooked.com/a/e5f31d5306834b25
– StoryTeller
Sep 5 at 8:53












sorry i kept the example short of course did include the defintion of my cachedobjectfactory
– AF_cpp
Sep 5 at 8:59





sorry i kept the example short of course did include the defintion of my cachedobjectfactory
– AF_cpp
Sep 5 at 8:59





3




3




@lubgr Here's a smaller example that also compiles: struct F; int main() std::make_unique<F>(); struct F;
– molbdnilo
Sep 5 at 8:59





@lubgr Here's a smaller example that also compiles: struct F; int main() std::make_unique<F>(); struct F;
– molbdnilo
Sep 5 at 8:59





1




1




@AF_cpp, you could remove all the first part of your question and only keep the mcve (starting with Or even simpler). It would improve the question.
– YSC
Sep 5 at 9:49




@AF_cpp, you could remove all the first part of your question and only keep the mcve (starting with Or even simpler). It would improve the question.
– YSC
Sep 5 at 9:49












2 Answers
2






active

oldest

votes

















up vote
15
down vote



accepted










make_unique has multiple points of instantiation: the end of the translation unit is also a point of instantiation. What you are seeing is the compiler only instantiating make_unique once CacheImpl/F is complete. Compilers are allowed to do this. Your code is ill-formed if you rely on it, and compilers are not required to detect the error.




14.6.4.1 Point of instantiation [temp.point]



8 A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. [...] If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.







share|improve this answer
















  • 1




    Great minds? : )
    – SkepticalEmpiricist
    Sep 5 at 9:31










  • plz 2 see comments on SkepticalEmpiricist's answer and provide el secondo opinion
    – Lightness Races in Orbit
    Sep 5 at 12:00






  • 2




    @LightnessRacesinOrbit You started with "But these two different points of instantiation do not give the template specialization different meanings according to the one definition rule." -- At the first point of instantiation, the template specialisation is ill-formed due to the invalid use of an incomplete type, and at the second, it is well-formed, right? Are you saying that that effectively requires compilers to use the second point of instantiation, or am I misunderstanding you?
    – hvd
    Sep 5 at 12:08






  • 1




    @hvd: I've asked a question about this: stackoverflow.com/questions/52185464/…
    – geza
    Sep 5 at 13:09










  • And additionally undefined by [res.on.functions]/2.5.
    – T.C.
    Sep 5 at 19:35

















up vote
11
down vote













The reason for why this compiles is here in [temp.point]¶8:




A specialization for a function template, a member function template,
or of a member function or static data member of a class template may
have multiple points of instantiations within a translation unit, and
in addition to the points of instantiation described above, for any
such specialization that has a point of instantiation within the
translation unit, the end of the translation unit is also considered a
point of instantiation.
A specialization for a class template has at
most one point of instantiation within a translation unit [...] If two different points of instantiation give a template specialization different meanings
according to the one-definition rule, the program is ill-formed, no diagnostic
required.




Do notice the ending of this quote, as we will get to it in the edit below, but for now what happens in practice per the OP's snippet is the compiler uses the additionally considered instantiation point of make_unique() that is placed at the end of the translation unit, so that it will have definitions that are missing at the original point of usage in the code. It is allowed to do so according to this clause from the spec.



Note this no longer compiles:



struct Foo; int main() std::make_unique<Foo>(); struct Foo ~Foo() = delete; ;


As in, the compiler doesn't miss on the point of instantiation, it only defers it in terms of which point in the translation unit it uses to generate code for the template.




Edit: Finally it seems that even though you have these multiple instantiation points, it doesn't mean that the behavior is defined if the definition is different between these points. Note the last sentence in the above quote, according to which this difference is defined by the One Definition Rule. This is taken straight from my comment to the answer by @hvd, who brought this to light here:
See here in the One Definition Rule:




Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program outside of a
discarded statement; no diagnostic required. The definition can appear
explicitly in the program, it can be found in the standard or a
user-defined library, or ...




And so in the OP's case, the're is obviously a difference between the two instantiation points, in that, as @hvd himself noted, the first one is of an incomplete type, and the second one isn't. Indeed, this difference constitutes two different definitions and so there's very little doubt this program is ill-formed.






share|improve this answer


















  • 1




    In other words, the standard mandates implementations to make this work, for magic.
    – Lightness Races in Orbit
    Sep 5 at 11:34










  • @LightnessRacesinOrbit: if hvd's answer is right, then it's quite the contrary. OP's program is ill-formed, shouldn't be written this way. Another compiler may not accept it.
    – geza
    Sep 5 at 11:42










  • @geza how about this new edit? I thought about it and figured this is a phrasing I can totally live with. Let me know if you think there's anything else missing.
    – SkepticalEmpiricist
    Sep 7 at 14:44










  • @SkepticalEmpiricist: yes, now your answer basically says the same thing as hvd's (and better explained). One question remains (at least, it's not clear for me): the explanation of why OP's code contains multiple definitions. Would make_unique<F> have multiple definitions, if make_unique were an empty function (this case, F's definition doesn't matter)?
    – geza
    Sep 7 at 20:11











  • @geza "Would make_unique<F> have multiple definitions, if ..." You mean would it have multiple instantiation points?
    – SkepticalEmpiricist
    Sep 8 at 13:59










Your Answer





StackExchange.ifUsing("editor", function ()
StackExchange.using("externalEditor", function ()
StackExchange.using("snippets", function ()
StackExchange.snippets.init();
);
);
, "code-snippets");

StackExchange.ready(function()
var channelOptions =
tags: "".split(" "),
id: "1"
;
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function()
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled)
StackExchange.using("snippets", function()
createEditor();
);

else
createEditor();

);

function createEditor()
StackExchange.prepareEditor(
heartbeatType: 'answer',
convertImagesToLinks: true,
noModals: false,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
);



);













 

draft saved


draft discarded


















StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52180744%2fpimpl-why-can-make-unique-be-called-on-an-incomplete-type%23new-answer', 'question_page');

);

Post as a guest






























2 Answers
2






active

oldest

votes








2 Answers
2






active

oldest

votes









active

oldest

votes






active

oldest

votes








up vote
15
down vote



accepted










make_unique has multiple points of instantiation: the end of the translation unit is also a point of instantiation. What you are seeing is the compiler only instantiating make_unique once CacheImpl/F is complete. Compilers are allowed to do this. Your code is ill-formed if you rely on it, and compilers are not required to detect the error.




14.6.4.1 Point of instantiation [temp.point]



8 A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. [...] If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.







share|improve this answer
















  • 1




    Great minds? : )
    – SkepticalEmpiricist
    Sep 5 at 9:31










  • plz 2 see comments on SkepticalEmpiricist's answer and provide el secondo opinion
    – Lightness Races in Orbit
    Sep 5 at 12:00






  • 2




    @LightnessRacesinOrbit You started with "But these two different points of instantiation do not give the template specialization different meanings according to the one definition rule." -- At the first point of instantiation, the template specialisation is ill-formed due to the invalid use of an incomplete type, and at the second, it is well-formed, right? Are you saying that that effectively requires compilers to use the second point of instantiation, or am I misunderstanding you?
    – hvd
    Sep 5 at 12:08






  • 1




    @hvd: I've asked a question about this: stackoverflow.com/questions/52185464/…
    – geza
    Sep 5 at 13:09










  • And additionally undefined by [res.on.functions]/2.5.
    – T.C.
    Sep 5 at 19:35














up vote
15
down vote



accepted










make_unique has multiple points of instantiation: the end of the translation unit is also a point of instantiation. What you are seeing is the compiler only instantiating make_unique once CacheImpl/F is complete. Compilers are allowed to do this. Your code is ill-formed if you rely on it, and compilers are not required to detect the error.




14.6.4.1 Point of instantiation [temp.point]



8 A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. [...] If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.







share|improve this answer
















  • 1




    Great minds? : )
    – SkepticalEmpiricist
    Sep 5 at 9:31










  • plz 2 see comments on SkepticalEmpiricist's answer and provide el secondo opinion
    – Lightness Races in Orbit
    Sep 5 at 12:00






  • 2




    @LightnessRacesinOrbit You started with "But these two different points of instantiation do not give the template specialization different meanings according to the one definition rule." -- At the first point of instantiation, the template specialisation is ill-formed due to the invalid use of an incomplete type, and at the second, it is well-formed, right? Are you saying that that effectively requires compilers to use the second point of instantiation, or am I misunderstanding you?
    – hvd
    Sep 5 at 12:08






  • 1




    @hvd: I've asked a question about this: stackoverflow.com/questions/52185464/…
    – geza
    Sep 5 at 13:09










  • And additionally undefined by [res.on.functions]/2.5.
    – T.C.
    Sep 5 at 19:35












up vote
15
down vote



accepted







up vote
15
down vote



accepted






make_unique has multiple points of instantiation: the end of the translation unit is also a point of instantiation. What you are seeing is the compiler only instantiating make_unique once CacheImpl/F is complete. Compilers are allowed to do this. Your code is ill-formed if you rely on it, and compilers are not required to detect the error.




14.6.4.1 Point of instantiation [temp.point]



8 A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. [...] If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.







share|improve this answer












make_unique has multiple points of instantiation: the end of the translation unit is also a point of instantiation. What you are seeing is the compiler only instantiating make_unique once CacheImpl/F is complete. Compilers are allowed to do this. Your code is ill-formed if you rely on it, and compilers are not required to detect the error.




14.6.4.1 Point of instantiation [temp.point]



8 A specialization for a function template, a member function template, or of a member function or static data member of a class template may have multiple points of instantiations within a translation unit, and in addition to the points of instantiation described above, for any such specialization that has a point of instantiation within the translation unit, the end of the translation unit is also considered a point of instantiation. [...] If two different points of instantiation give a template specialization different meanings according to the one definition rule (3.2), the program is ill-formed, no diagnostic required.








share|improve this answer












share|improve this answer



share|improve this answer










answered Sep 5 at 9:14









hvd

113k11196273




113k11196273







  • 1




    Great minds? : )
    – SkepticalEmpiricist
    Sep 5 at 9:31










  • plz 2 see comments on SkepticalEmpiricist's answer and provide el secondo opinion
    – Lightness Races in Orbit
    Sep 5 at 12:00






  • 2




    @LightnessRacesinOrbit You started with "But these two different points of instantiation do not give the template specialization different meanings according to the one definition rule." -- At the first point of instantiation, the template specialisation is ill-formed due to the invalid use of an incomplete type, and at the second, it is well-formed, right? Are you saying that that effectively requires compilers to use the second point of instantiation, or am I misunderstanding you?
    – hvd
    Sep 5 at 12:08






  • 1




    @hvd: I've asked a question about this: stackoverflow.com/questions/52185464/…
    – geza
    Sep 5 at 13:09










  • And additionally undefined by [res.on.functions]/2.5.
    – T.C.
    Sep 5 at 19:35












  • 1




    Great minds? : )
    – SkepticalEmpiricist
    Sep 5 at 9:31










  • plz 2 see comments on SkepticalEmpiricist's answer and provide el secondo opinion
    – Lightness Races in Orbit
    Sep 5 at 12:00






  • 2




    @LightnessRacesinOrbit You started with "But these two different points of instantiation do not give the template specialization different meanings according to the one definition rule." -- At the first point of instantiation, the template specialisation is ill-formed due to the invalid use of an incomplete type, and at the second, it is well-formed, right? Are you saying that that effectively requires compilers to use the second point of instantiation, or am I misunderstanding you?
    – hvd
    Sep 5 at 12:08






  • 1




    @hvd: I've asked a question about this: stackoverflow.com/questions/52185464/…
    – geza
    Sep 5 at 13:09










  • And additionally undefined by [res.on.functions]/2.5.
    – T.C.
    Sep 5 at 19:35







1




1




Great minds? : )
– SkepticalEmpiricist
Sep 5 at 9:31




Great minds? : )
– SkepticalEmpiricist
Sep 5 at 9:31












plz 2 see comments on SkepticalEmpiricist's answer and provide el secondo opinion
– Lightness Races in Orbit
Sep 5 at 12:00




plz 2 see comments on SkepticalEmpiricist's answer and provide el secondo opinion
– Lightness Races in Orbit
Sep 5 at 12:00




2




2




@LightnessRacesinOrbit You started with "But these two different points of instantiation do not give the template specialization different meanings according to the one definition rule." -- At the first point of instantiation, the template specialisation is ill-formed due to the invalid use of an incomplete type, and at the second, it is well-formed, right? Are you saying that that effectively requires compilers to use the second point of instantiation, or am I misunderstanding you?
– hvd
Sep 5 at 12:08




@LightnessRacesinOrbit You started with "But these two different points of instantiation do not give the template specialization different meanings according to the one definition rule." -- At the first point of instantiation, the template specialisation is ill-formed due to the invalid use of an incomplete type, and at the second, it is well-formed, right? Are you saying that that effectively requires compilers to use the second point of instantiation, or am I misunderstanding you?
– hvd
Sep 5 at 12:08




1




1




@hvd: I've asked a question about this: stackoverflow.com/questions/52185464/…
– geza
Sep 5 at 13:09




@hvd: I've asked a question about this: stackoverflow.com/questions/52185464/…
– geza
Sep 5 at 13:09












And additionally undefined by [res.on.functions]/2.5.
– T.C.
Sep 5 at 19:35




And additionally undefined by [res.on.functions]/2.5.
– T.C.
Sep 5 at 19:35












up vote
11
down vote













The reason for why this compiles is here in [temp.point]¶8:




A specialization for a function template, a member function template,
or of a member function or static data member of a class template may
have multiple points of instantiations within a translation unit, and
in addition to the points of instantiation described above, for any
such specialization that has a point of instantiation within the
translation unit, the end of the translation unit is also considered a
point of instantiation.
A specialization for a class template has at
most one point of instantiation within a translation unit [...] If two different points of instantiation give a template specialization different meanings
according to the one-definition rule, the program is ill-formed, no diagnostic
required.




Do notice the ending of this quote, as we will get to it in the edit below, but for now what happens in practice per the OP's snippet is the compiler uses the additionally considered instantiation point of make_unique() that is placed at the end of the translation unit, so that it will have definitions that are missing at the original point of usage in the code. It is allowed to do so according to this clause from the spec.



Note this no longer compiles:



struct Foo; int main() std::make_unique<Foo>(); struct Foo ~Foo() = delete; ;


As in, the compiler doesn't miss on the point of instantiation, it only defers it in terms of which point in the translation unit it uses to generate code for the template.




Edit: Finally it seems that even though you have these multiple instantiation points, it doesn't mean that the behavior is defined if the definition is different between these points. Note the last sentence in the above quote, according to which this difference is defined by the One Definition Rule. This is taken straight from my comment to the answer by @hvd, who brought this to light here:
See here in the One Definition Rule:




Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program outside of a
discarded statement; no diagnostic required. The definition can appear
explicitly in the program, it can be found in the standard or a
user-defined library, or ...




And so in the OP's case, the're is obviously a difference between the two instantiation points, in that, as @hvd himself noted, the first one is of an incomplete type, and the second one isn't. Indeed, this difference constitutes two different definitions and so there's very little doubt this program is ill-formed.






share|improve this answer


















  • 1




    In other words, the standard mandates implementations to make this work, for magic.
    – Lightness Races in Orbit
    Sep 5 at 11:34










  • @LightnessRacesinOrbit: if hvd's answer is right, then it's quite the contrary. OP's program is ill-formed, shouldn't be written this way. Another compiler may not accept it.
    – geza
    Sep 5 at 11:42










  • @geza how about this new edit? I thought about it and figured this is a phrasing I can totally live with. Let me know if you think there's anything else missing.
    – SkepticalEmpiricist
    Sep 7 at 14:44










  • @SkepticalEmpiricist: yes, now your answer basically says the same thing as hvd's (and better explained). One question remains (at least, it's not clear for me): the explanation of why OP's code contains multiple definitions. Would make_unique<F> have multiple definitions, if make_unique were an empty function (this case, F's definition doesn't matter)?
    – geza
    Sep 7 at 20:11











  • @geza "Would make_unique<F> have multiple definitions, if ..." You mean would it have multiple instantiation points?
    – SkepticalEmpiricist
    Sep 8 at 13:59














up vote
11
down vote













The reason for why this compiles is here in [temp.point]¶8:




A specialization for a function template, a member function template,
or of a member function or static data member of a class template may
have multiple points of instantiations within a translation unit, and
in addition to the points of instantiation described above, for any
such specialization that has a point of instantiation within the
translation unit, the end of the translation unit is also considered a
point of instantiation.
A specialization for a class template has at
most one point of instantiation within a translation unit [...] If two different points of instantiation give a template specialization different meanings
according to the one-definition rule, the program is ill-formed, no diagnostic
required.




Do notice the ending of this quote, as we will get to it in the edit below, but for now what happens in practice per the OP's snippet is the compiler uses the additionally considered instantiation point of make_unique() that is placed at the end of the translation unit, so that it will have definitions that are missing at the original point of usage in the code. It is allowed to do so according to this clause from the spec.



Note this no longer compiles:



struct Foo; int main() std::make_unique<Foo>(); struct Foo ~Foo() = delete; ;


As in, the compiler doesn't miss on the point of instantiation, it only defers it in terms of which point in the translation unit it uses to generate code for the template.




Edit: Finally it seems that even though you have these multiple instantiation points, it doesn't mean that the behavior is defined if the definition is different between these points. Note the last sentence in the above quote, according to which this difference is defined by the One Definition Rule. This is taken straight from my comment to the answer by @hvd, who brought this to light here:
See here in the One Definition Rule:




Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program outside of a
discarded statement; no diagnostic required. The definition can appear
explicitly in the program, it can be found in the standard or a
user-defined library, or ...




And so in the OP's case, the're is obviously a difference between the two instantiation points, in that, as @hvd himself noted, the first one is of an incomplete type, and the second one isn't. Indeed, this difference constitutes two different definitions and so there's very little doubt this program is ill-formed.






share|improve this answer


















  • 1




    In other words, the standard mandates implementations to make this work, for magic.
    – Lightness Races in Orbit
    Sep 5 at 11:34










  • @LightnessRacesinOrbit: if hvd's answer is right, then it's quite the contrary. OP's program is ill-formed, shouldn't be written this way. Another compiler may not accept it.
    – geza
    Sep 5 at 11:42










  • @geza how about this new edit? I thought about it and figured this is a phrasing I can totally live with. Let me know if you think there's anything else missing.
    – SkepticalEmpiricist
    Sep 7 at 14:44










  • @SkepticalEmpiricist: yes, now your answer basically says the same thing as hvd's (and better explained). One question remains (at least, it's not clear for me): the explanation of why OP's code contains multiple definitions. Would make_unique<F> have multiple definitions, if make_unique were an empty function (this case, F's definition doesn't matter)?
    – geza
    Sep 7 at 20:11











  • @geza "Would make_unique<F> have multiple definitions, if ..." You mean would it have multiple instantiation points?
    – SkepticalEmpiricist
    Sep 8 at 13:59












up vote
11
down vote










up vote
11
down vote









The reason for why this compiles is here in [temp.point]¶8:




A specialization for a function template, a member function template,
or of a member function or static data member of a class template may
have multiple points of instantiations within a translation unit, and
in addition to the points of instantiation described above, for any
such specialization that has a point of instantiation within the
translation unit, the end of the translation unit is also considered a
point of instantiation.
A specialization for a class template has at
most one point of instantiation within a translation unit [...] If two different points of instantiation give a template specialization different meanings
according to the one-definition rule, the program is ill-formed, no diagnostic
required.




Do notice the ending of this quote, as we will get to it in the edit below, but for now what happens in practice per the OP's snippet is the compiler uses the additionally considered instantiation point of make_unique() that is placed at the end of the translation unit, so that it will have definitions that are missing at the original point of usage in the code. It is allowed to do so according to this clause from the spec.



Note this no longer compiles:



struct Foo; int main() std::make_unique<Foo>(); struct Foo ~Foo() = delete; ;


As in, the compiler doesn't miss on the point of instantiation, it only defers it in terms of which point in the translation unit it uses to generate code for the template.




Edit: Finally it seems that even though you have these multiple instantiation points, it doesn't mean that the behavior is defined if the definition is different between these points. Note the last sentence in the above quote, according to which this difference is defined by the One Definition Rule. This is taken straight from my comment to the answer by @hvd, who brought this to light here:
See here in the One Definition Rule:




Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program outside of a
discarded statement; no diagnostic required. The definition can appear
explicitly in the program, it can be found in the standard or a
user-defined library, or ...




And so in the OP's case, the're is obviously a difference between the two instantiation points, in that, as @hvd himself noted, the first one is of an incomplete type, and the second one isn't. Indeed, this difference constitutes two different definitions and so there's very little doubt this program is ill-formed.






share|improve this answer














The reason for why this compiles is here in [temp.point]¶8:




A specialization for a function template, a member function template,
or of a member function or static data member of a class template may
have multiple points of instantiations within a translation unit, and
in addition to the points of instantiation described above, for any
such specialization that has a point of instantiation within the
translation unit, the end of the translation unit is also considered a
point of instantiation.
A specialization for a class template has at
most one point of instantiation within a translation unit [...] If two different points of instantiation give a template specialization different meanings
according to the one-definition rule, the program is ill-formed, no diagnostic
required.




Do notice the ending of this quote, as we will get to it in the edit below, but for now what happens in practice per the OP's snippet is the compiler uses the additionally considered instantiation point of make_unique() that is placed at the end of the translation unit, so that it will have definitions that are missing at the original point of usage in the code. It is allowed to do so according to this clause from the spec.



Note this no longer compiles:



struct Foo; int main() std::make_unique<Foo>(); struct Foo ~Foo() = delete; ;


As in, the compiler doesn't miss on the point of instantiation, it only defers it in terms of which point in the translation unit it uses to generate code for the template.




Edit: Finally it seems that even though you have these multiple instantiation points, it doesn't mean that the behavior is defined if the definition is different between these points. Note the last sentence in the above quote, according to which this difference is defined by the One Definition Rule. This is taken straight from my comment to the answer by @hvd, who brought this to light here:
See here in the One Definition Rule:




Every program shall contain exactly one definition of every non-inline
function or variable that is odr-used in that program outside of a
discarded statement; no diagnostic required. The definition can appear
explicitly in the program, it can be found in the standard or a
user-defined library, or ...




And so in the OP's case, the're is obviously a difference between the two instantiation points, in that, as @hvd himself noted, the first one is of an incomplete type, and the second one isn't. Indeed, this difference constitutes two different definitions and so there's very little doubt this program is ill-formed.







share|improve this answer














share|improve this answer



share|improve this answer








edited Sep 18 at 18:05

























answered Sep 5 at 9:15









SkepticalEmpiricist

4,628924




4,628924







  • 1




    In other words, the standard mandates implementations to make this work, for magic.
    – Lightness Races in Orbit
    Sep 5 at 11:34










  • @LightnessRacesinOrbit: if hvd's answer is right, then it's quite the contrary. OP's program is ill-formed, shouldn't be written this way. Another compiler may not accept it.
    – geza
    Sep 5 at 11:42










  • @geza how about this new edit? I thought about it and figured this is a phrasing I can totally live with. Let me know if you think there's anything else missing.
    – SkepticalEmpiricist
    Sep 7 at 14:44










  • @SkepticalEmpiricist: yes, now your answer basically says the same thing as hvd's (and better explained). One question remains (at least, it's not clear for me): the explanation of why OP's code contains multiple definitions. Would make_unique<F> have multiple definitions, if make_unique were an empty function (this case, F's definition doesn't matter)?
    – geza
    Sep 7 at 20:11











  • @geza "Would make_unique<F> have multiple definitions, if ..." You mean would it have multiple instantiation points?
    – SkepticalEmpiricist
    Sep 8 at 13:59












  • 1




    In other words, the standard mandates implementations to make this work, for magic.
    – Lightness Races in Orbit
    Sep 5 at 11:34










  • @LightnessRacesinOrbit: if hvd's answer is right, then it's quite the contrary. OP's program is ill-formed, shouldn't be written this way. Another compiler may not accept it.
    – geza
    Sep 5 at 11:42










  • @geza how about this new edit? I thought about it and figured this is a phrasing I can totally live with. Let me know if you think there's anything else missing.
    – SkepticalEmpiricist
    Sep 7 at 14:44










  • @SkepticalEmpiricist: yes, now your answer basically says the same thing as hvd's (and better explained). One question remains (at least, it's not clear for me): the explanation of why OP's code contains multiple definitions. Would make_unique<F> have multiple definitions, if make_unique were an empty function (this case, F's definition doesn't matter)?
    – geza
    Sep 7 at 20:11











  • @geza "Would make_unique<F> have multiple definitions, if ..." You mean would it have multiple instantiation points?
    – SkepticalEmpiricist
    Sep 8 at 13:59







1




1




In other words, the standard mandates implementations to make this work, for magic.
– Lightness Races in Orbit
Sep 5 at 11:34




In other words, the standard mandates implementations to make this work, for magic.
– Lightness Races in Orbit
Sep 5 at 11:34












@LightnessRacesinOrbit: if hvd's answer is right, then it's quite the contrary. OP's program is ill-formed, shouldn't be written this way. Another compiler may not accept it.
– geza
Sep 5 at 11:42




@LightnessRacesinOrbit: if hvd's answer is right, then it's quite the contrary. OP's program is ill-formed, shouldn't be written this way. Another compiler may not accept it.
– geza
Sep 5 at 11:42












@geza how about this new edit? I thought about it and figured this is a phrasing I can totally live with. Let me know if you think there's anything else missing.
– SkepticalEmpiricist
Sep 7 at 14:44




@geza how about this new edit? I thought about it and figured this is a phrasing I can totally live with. Let me know if you think there's anything else missing.
– SkepticalEmpiricist
Sep 7 at 14:44












@SkepticalEmpiricist: yes, now your answer basically says the same thing as hvd's (and better explained). One question remains (at least, it's not clear for me): the explanation of why OP's code contains multiple definitions. Would make_unique<F> have multiple definitions, if make_unique were an empty function (this case, F's definition doesn't matter)?
– geza
Sep 7 at 20:11





@SkepticalEmpiricist: yes, now your answer basically says the same thing as hvd's (and better explained). One question remains (at least, it's not clear for me): the explanation of why OP's code contains multiple definitions. Would make_unique<F> have multiple definitions, if make_unique were an empty function (this case, F's definition doesn't matter)?
– geza
Sep 7 at 20:11













@geza "Would make_unique<F> have multiple definitions, if ..." You mean would it have multiple instantiation points?
– SkepticalEmpiricist
Sep 8 at 13:59




@geza "Would make_unique<F> have multiple definitions, if ..." You mean would it have multiple instantiation points?
– SkepticalEmpiricist
Sep 8 at 13:59

















 

draft saved


draft discarded















































 


draft saved


draft discarded














StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f52180744%2fpimpl-why-can-make-unique-be-called-on-an-incomplete-type%23new-answer', 'question_page');

);

Post as a guest













































































Popular posts from this blog

Peggy Mitchell

Palaiologos

The Forum (Inglewood, California)