Unqualified lookup fails...

Discussion in 'C++' started by Werner, Jul 26, 2012.

  1. Werner

    Werner Guest

    Hi All,

    The following code fails to compile on GCC 4.7.
    I've now learned because of its compliance. I've
    also noticing it failing under Comeau. On moving
    the functions in the anonymous namespace above the
    definition of Xx, it compiles:

    ------------------------------------------------
    template <class T>
    struct Xx
    {
    Xx()
    {
    T inst;
    int n = sequenceSize( inst );
    }
    };


    struct Header1
    {
    int sequenceSize_;
    };

    struct Header2
    {
    int itemCount_;
    };

    namespace {
    int sequenceSize( const Header1& h )
    {
    return h.sequenceSize_;
    }
    int sequenceSize( const Header2& h )
    {
    return h.itemCount_;
    }
    }


    void testUnqualifiedLookup()
    {
    Xx<Header1> xH1;
    Xx<Header2> xH2;
    }

    Now (the question):

    What would be the best way to fix this kind of problem?

    A couple of options exist:

    1) Functions sequenceSize would be found if they were
    in the same namespaces as classes "Headerx" (by ADL).

    2) A declaration of sequenceSize need at least exist
    prior to the template definition.

    I now have a case like this:

    struct MsgX
    {
    int sequenceSizeOfA_;
    int sequenceSizeOfB_;

    std::vector<A> a_;
    std::vector<B> b_;
    };

    I still would like the calling code (in the template
    to work, therefore:

    //Translation Unit A:
    namespace {
    int sequenceSize( const MsgX& msg )
    {
    return msg.sequenceSizeOfA_;
    }
    }
    #include "Xx.h"

    //Translation Unit B:
    namespace {
    int sequenceSize( const MsgX& msg )
    {
    return msg.sequenceSizeOfB_;
    }
    }
    #include "Xx.h"

    .... but this makes the compilation dependent on
    the order of inclusion. Also, in this case
    I cannot use ADL, because in both instances
    of sequenceSz, the argument type is the same.

    Now the question:

    1) Is it bad of Xx to depend on an unqualified name?

    2) If I'm not able to change Xx, what would be a good
    solution - to include the declaration of sequenceSize
    above Xx?

    3) If I am allowed to change Xx, what would you do?

    Kind regards,

    Werner
    Werner, Jul 26, 2012
    #1
    1. Advertising

  2. Werner

    SG Guest

    On Jul 26, 1:22 pm, Werner wrote:

    > ------------------------------------------------
    > template <class T>
    > struct Xx
    > {
    >   Xx()
    >   {
    >     T inst;
    >     int n = sequenceSize( inst );
    >   }
    >
    > };


    [snip]

    > A couple of options exist:
    >
    > 1) Functions sequenceSize would be found if they were
    > in the same namespaces as classes "Headerx" (by ADL).


    This sounds like the way to go.

    > 2) A declaration of sequenceSize need at least exist
    > prior to the template definition.


    No, don't do that. Rely on ADL.

    > I now have a case like this:
    >
    > struct MsgX
    > {
    >   int sequenceSizeOfA_;
    >   int sequenceSizeOfB_;
    >   std::vector<A> a_;
    >   std::vector<B> b_;
    > };
    >
    > I still would like the calling code (in the template
    > to work, therefore:
    >
    > //Translation Unit A:
    > namespace {
    >   int sequenceSize( const MsgX& msg )
    >   {
    >     return msg.sequenceSizeOfA_;
    >   }}
    >
    > #include "Xx.h"
    >
    > //Translation Unit B:
    > namespace {
    >   int sequenceSize( const MsgX& msg )
    >   {
    >     return msg.sequenceSizeOfB_;
    >   }}
    >
    > #include "Xx.h"
    >
    > ... but this makes the compilation dependent on
    > the order of inclusion.


    Not only that. I believe it is also a violation of the ODR (one
    definition rule) since you rely on two different versions of
    Xx<MessageX>::Xx. So, this is not even an option unless you are fine
    with unedfined behaviour.

    > Also, in this case
    > I cannot use ADL, because in both instances
    > of sequenceSz, the argument type is the same.


    You could use a wrapper type + ADL:

    struct wrapper_MsgX_A
    {
    MsX data;
    };

    int sequenceSize(wrapper_MsgX_A const& x)
    { return x.data.sequenceSizeOfA_; }

    ...

    Xx<wrapper_MsgX_A> foo;

    > Now the question:
    >
    > 1) Is it bad of Xx to depend on an unqualified name?


    No. It's good since it allows ADL and ADL is a good thing.

    > 2) If I'm not able to change Xx, what would be a good
    > solution - to include the declaration of sequenceSize
    > above Xx?


    No.

    > 3) If I am allowed to change Xx, what would you do?


    Keep it as it is or introduce another layer of indirection via a
    traits
    class or additional template parameter for Xx.


    Cheers!
    SG
    SG, Jul 26, 2012
    #2
    1. Advertising

  3. On 7/26/2012 7:22 AM, Werner wrote:
    > The following code fails to compile on GCC 4.7.
    > I've now learned because of its compliance. I've
    > also noticing it failing under Comeau. On moving
    > the functions in the anonymous namespace above the
    > definition of Xx, it compiles:
    >
    > ------------------------------------------------
    > template <class T>
    > struct Xx
    > {
    > Xx()
    > {
    > T inst;
    > int n = sequenceSize( inst );
    > }
    > };
    >
    >
    > struct Header1
    > {
    > int sequenceSize_;
    > };
    >
    > struct Header2
    > {
    > int itemCount_;
    > };
    >
    > namespace {
    > int sequenceSize( const Header1& h )
    > {
    > return h.sequenceSize_;
    > }
    > int sequenceSize( const Header2& h )
    > {
    > return h.itemCount_;
    > }
    > }
    >
    >
    > void testUnqualifiedLookup()
    > {
    > Xx<Header1> xH1;
    > Xx<Header2> xH2;
    > }


    So, the function 'sequenceSize' used in the template has to have been
    declared at the point of its first encounter by the compiler. Can you
    declare it somehow without defining? See below for different examples.

    > Now (the question):
    >
    > What would be the best way to fix this kind of problem?


    "The best" depends on the definition of "good" and the criteria for
    comparing two "good" solutions.

    > A couple of options exist:
    >
    > 1) Functions sequenceSize would be found if they were
    > in the same namespaces as classes "Headerx" (by ADL).


    Which is a very good idea, BTW. Those need to be members, which would
    make them class-specific or instance-specific, which is essentially what
    they are now anyway. Of course that would make the classes or instances
    "sequence-aware", IOW there will be some design dependency between the
    class and the concept of "sequence" or "sequence size". Is that bad?

    > 2) A declaration of sequenceSize need at least exist
    > prior to the template definition.


    Yes, that's the first thing that came to my mind.

    > I now have a case like this:
    >
    > struct MsgX
    > {
    > int sequenceSizeOfA_;
    > int sequenceSizeOfB_;
    >
    > std::vector<A> a_;
    > std::vector<B> b_;
    > };
    >
    > I still would like the calling code (in the template
    > to work, therefore:
    >
    > //Translation Unit A:
    > namespace {
    > int sequenceSize( const MsgX& msg )
    > {
    > return msg.sequenceSizeOfA_;
    > }
    > }
    > #include "Xx.h"
    >
    > //Translation Unit B:
    > namespace {
    > int sequenceSize( const MsgX& msg )
    > {
    > return msg.sequenceSizeOfB_;
    > }
    > }
    > #include "Xx.h"
    >
    > ... but this makes the compilation dependent on
    > the order of inclusion. Also, in this case
    > I cannot use ADL, because in both instances
    > of sequenceSz, the argument type is the same.
    >
    > Now the question:
    >
    > 1) Is it bad of Xx to depend on an unqualified name?


    This is too general a question to be answered with certainty, so the
    answer is "it depends". And there are too many factors to list, IMO.

    > 2) If I'm not able to change Xx, what would be a good
    > solution - to include the declaration of sequenceSize
    > above Xx?


    You can always wrap the header in which Xx comes in your own header and
    include anything before and/or after that. Depending on your build
    environment there are ways to hide the original header from being
    included "by mistake".

    > 3) If I am allowed to change Xx, what would you do?


    I'd tell you to go for it <g>... On a serious note, I don't know.
    Again, it depends. You need to look at the overall design of your
    system and see whether it makes sense in your case to make 'Header1' and
    'Header2' classes to have an inlined member function 'int
    sequenceSize()' that would return the respective values.

    Here is another solution, and if you can't change the header in which Xx
    template resides, wrap it to add the declaration:

    // -- inside the wrapper header
    namespace {
    template<class T> int sequenceSize(const T&);
    }
    // -- the wrapping header includes the Xx header:
    template <class T>
    struct Xx
    {
    int m_size;
    Xx() : m_size(sequenceSize(T()))
    {
    }
    };
    // ---- end of the wrapper

    struct Header1
    {
    int sequenceSize_;
    };

    struct Header2
    {
    int itemCount_;
    };

    namespace {
    template<>
    int sequenceSize<Header1>( const Header1& h )
    {
    return h.sequenceSize_;
    }
    template<>
    int sequenceSize<Header2>( const Header2& h )
    {
    return h.itemCount_;
    }
    }

    int main()
    {
    Xx<Header1> xH1;
    Xx<Header2> xH2;
    }

    Keep in mind that if you don't need your 'sequenceSize' specializations
    in the unnamed namespace, remove the declaration of the template from it
    as well. That's what I'd do anyway.

    V
    --
    I do not respond to top-posted replies, please don't ask
    Victor Bazarov, Jul 26, 2012
    #3
  4. Werner

    Werner Guest

    On Thursday, July 26, 2012 1:44:32 PM UTC+2, SG wrote:

    > Not only that. I believe it is also a violation of the ODR (one
    > definition rule) since you rely on two different versions of
    > Xx&lt;MessageX&gt;::Xx. So, this is not even an option unless you are fine
    > with unedfined behaviour.


    Hmmm. I did not even think of that possibility. This would then
    probably be why they decided to have that rule in the first place
    (declaration visible at point of definition, as opposed to point
    of instantiation). I've discovered this when moving to GCC 4.7.

    > Keep it as it is or introduce another layer of indirection via a
    > traits
    > class or additional template parameter for Xx.


    Yes, in fact Xx has an additional template parameter defining the
    data, but is was not used in sequenceSize.

    I've now resolved to this:

    template <class HdrT, class DataT>
    struct HdrSequenceMsgModel
    {
    HdrSequenceMsgModel()
    {
    HdrT inst;
    std::size_t n = sequenceSize( inst, boost::mpl::identity<DataT>() );
    }
    };

    and...

    namespace SomeSpace{
    class Header;
    class Data;
    std::size_t sequenceSize( const Header&, boost::mpl::identity<Data> );
    };

    Thank you,

    Regards,

    W
    Werner, Jul 26, 2012
    #4
  5. Werner

    Werner Guest

    On Jul 26, 1:57 pm, Victor Bazarov <> wrote:

    [snip]

    > Here is another solution, and if you can't change the header in which Xx
    > template resides, wrap it to add the declaration:
    >
    >     // -- inside the wrapper header
    >     namespace {
    >        template<class T> int sequenceSize(const T&);
    >     }
    >     // -- the wrapping header includes the Xx header:
    >     template <class T>
    >     struct Xx
    >     {
    >        int m_size;
    >        Xx() : m_size(sequenceSize(T()))
    >        {
    >        }
    >     };
    >     // ---- end of the wrapper
    >
    >     struct Header1
    >     {
    >        int sequenceSize_;
    >     };
    >
    >     struct Header2
    >     {
    >        int itemCount_;
    >     };
    >
    >     namespace {
    >        template<>
    >        int sequenceSize<Header1>( const Header1& h )
    >        {
    >           return h.sequenceSize_;
    >        }
    >        template<>
    >        int sequenceSize<Header2>( const Header2& h )
    >        {
    >           return h.itemCount_;
    >        }
    >     }
    >
    >     int main()
    >     {
    >        Xx<Header1> xH1;
    >        Xx<Header2> xH2;
    >     }
    >
    > Keep in mind that if you don't need your 'sequenceSize' specializations
    > in the unnamed namespace, remove the declaration of the template from it
    > as well.  That's what I'd do anyway.


    Interesting idea. I haven't thought of using a wrapper header. It
    would still not work for the last case documented, where one
    has one type containing two lists:

    struct MsgX
    {
    int sequenceSizeOfA_;
    int sequenceSizeOfB_;

    std::vector<A> a_;
    std::vector<B> b_;
    };

    ....but it is certainly a nice solution for similar problems
    if one is not able to modify the template.

    Thanks,

    Regards, Werner
    Werner, Jul 26, 2012
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. C. M. Sperberg-McQueen
    Replies:
    0
    Views:
    772
    C. M. Sperberg-McQueen
    Jul 31, 2003
  2. Mavis
    Replies:
    1
    Views:
    322
    Mavis
    Jun 2, 2006
  3. mavis
    Replies:
    3
    Views:
    3,576
    mavis
    Jun 2, 2006
  4. j vickroy
    Replies:
    1
    Views:
    1,127
    j vickroy
    Oct 8, 2003
  5. Replies:
    4
    Views:
    371
Loading...

Share This Page